package com.ruoyi.depot.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.ruoyi.commodity.domain.Product;
import com.ruoyi.commodity.domain.ProductImage;
import com.ruoyi.commodity.domain.vo.ProductVO;
import com.ruoyi.commodity.mapper.ProductImageMapper;
import com.ruoyi.commodity.mapper.ProductMapper;
import com.ruoyi.common.api.localInventory.dto.LocalInventoryDto;
import com.ruoyi.common.api.procuredStorage.ProcuredStorageApi;
import com.ruoyi.common.api.procuredStorage.dto.ProcuredStorageDto;
import com.ruoyi.common.api.walmartStockPlan.WalmartStockPlanApi;
import com.ruoyi.common.api.walmartStockPlan.dto.PaymentReturnDto;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.ProcuredOrders;
import com.ruoyi.common.enums.InventoryChangeType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.depot.domain.*;
import com.ruoyi.depot.domain.vo.LocalInventoryVO;
import com.ruoyi.depot.mapper.*;
import com.ruoyi.procured.mapper.ProcuredOrdersMapper;
import com.ruoyi.sale.domain.*;
import com.ruoyi.sale.mapper.*;
import com.ruoyi.depot.service.IWalmartStockPlanService;
import com.ruoyi.walmart.domain.auth.ShopAuth;
import com.ruoyi.walmart.domain.shipments.*;
import com.ruoyi.walmart.domain.shipments.dto.ShipmentStockFilters;
import com.ruoyi.walmart.dto.ShipmentItemDTO;
import com.ruoyi.walmart.mapper.Items.PairProductsMapper;
import com.ruoyi.walmart.mapper.auth.ShopAuthMapper;
import com.ruoyi.walmart.mapper.shipments.ShipmentItemMapper;
import com.ruoyi.walmart.mapper.shipments.ShipmentMapper;
import com.ruoyi.walmart.mapper.shipments.ShipmentStockMapper;
import com.ruoyi.walmart.service.shipments.ShipmentItemService;
import com.ruoyi.walmart.util.walmartUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * walmart备货计划Service业务层处理
 *
 * @author ruoyi
 * @date 2025-02-26
 */
@Service
public class WalmartStockPlanServiceImpl implements IWalmartStockPlanService, WalmartStockPlanApi {

	private static final BigDecimal KG_DIVISOR = new BigDecimal("6000");  // 计费模式为KG的时候需要使用到的常量
	private static final BigDecimal M3_DIVISOR = new BigDecimal("1000000");  // 计费模式为m³的时候需要使用到的常量
	private static final BigDecimal M3_WEIGHT_CONVERSION_FACTOR = new BigDecimal("363");  // 计费模式为m³的时候需要使用到的常量
	private static final int CALCULATION_SCALE = 6; // 中间除法计算的精度
	@Autowired
	private WalmartStockPlanMapper walmartStockPlanMapper;
	@Autowired
	private WalmartStockProductDetailsMapper walmartStockProductDetailsMapper;
	@Autowired
	private ShipmentsProductMapper shipmentsProductMapper;
	@Autowired
	private PlanMapper planMapper;
	@Autowired
	private ProductImageMapper productImageMapper;
	@Autowired
	private WalmartStockPlanPackingListMapper walmartStockPlanPackingListMapper;
	@Autowired
	private LocalInventoryMapper localInventoryMapper;
	@Autowired
	private LocalInventoryDetailsMapper localInventoryDetailsMapper;
	@Autowired
	private WalmartStockPlanPackingDetailMapper packingDetailMapper;
	@Autowired
	private ShipmentItemMapper shipmentItemMapper;
	@Autowired
	private ProductMapper productMapper;
	@Autowired
	private InboundStockMapper inboundStockMapper;
	@Autowired
	private OutboundStockLogMapper outboundStockLogMapper;
	@Autowired
	private ProcuredOrdersMapper procuredOrdersMapper;
	@Autowired
	private ShipmentItemService shipmentItemService;
	@Autowired
	private ShipmentStockMapper shipmentStockMapper;
	@Autowired
	private ShipmentMapper shipmentMapper;
	@Autowired
	private OutboundOrderMapper outboundOrderMapper;
	@Autowired
	private OutboundOrderDetailMapper outboundOrderDetailMapper;
	@Autowired
	private ProcuredStorageApi procuredStorageApi;
	private static final Map<String, String> SKU_MAPPING = new HashMap<>();

	static {
		// 初始化映射关系
		SKU_MAPPING.put("CC10021-01", "HG0069-02");
		SKU_MAPPING.put("CC10022", "HG0073");
		SKU_MAPPING.put("CC10017", "HG0074-01");
		SKU_MAPPING.put("CC10023", "HG0075");
		SKU_MAPPING.put("CC10015", "HG0077-01");
		SKU_MAPPING.put("CC10014-01", "HG0078-02");
		SKU_MAPPING.put("CC10024", "HG0079");
		SKU_MAPPING.put("CC10012-01", "HG0081-02");
		SKU_MAPPING.put("CC10025", "HG0082");
		SKU_MAPPING.put("CC10026", "HG0083");
	}
	/**
	 * 查询walmart备货计划
	 *
	 * @param stockId walmart备货计划主键
	 * @return walmart备货计划
	 */
	@Override
	public WalmartStockPlan selectWalmartStockPlanByStockId(String stockId) {

		// 查询出备货计划
		WalmartStockPlan walmartStockPlan = walmartStockPlanMapper.selectWalmartStockPlanByStockId(stockId);

		// 根据 stockIds 批量查询所有备货计划的明细
		List<WalmartStockProductDetails> details = walmartStockProductDetailsMapper.selectList(new LambdaQueryWrapper<>(WalmartStockProductDetails.class)
				.eq(WalmartStockProductDetails::getStockId, stockId));

		// 获取所有 shipmentsProductId
		List<String> shipmentsProductIds = details.stream()
				.map(WalmartStockProductDetails::getShipmentsProductId)
				.collect(Collectors.toList());


		// 根据 shipmentsProductIds 批量查询所有 ShipmentsProduct 数据
		List<ShipmentsProduct> shipmentsProducts = shipmentsProductMapper.selectListByIds(shipmentsProductIds);

		// 提取所有的产品id
		Map<Integer, List<String>> collect = productImageMapper.selectProductImagesByProductId(shipmentsProducts.stream().map(ShipmentsProduct::getProductId).collect(Collectors.toList()))
				.stream().collect(Collectors.groupingBy(ProductImage::getProductId, Collectors.mapping(ProductImage::getProductImagePath, Collectors.toList())));

		// 将产品图片路径封装到每一个详细中去
		shipmentsProducts.forEach(
				e -> e.setImgName(collect.get(e.getProductId().intValue()))
		);

		// 获取所有 Plan 数据，批量查询
		List<Plan> planList = planMapper.selectPlanByIds(shipmentsProducts.stream().map(ShipmentsProduct::getPlanId).collect(Collectors.toList()));

		// 根据 Plan ID 分组 ShipmentsProduct 数据
		Map<Long, List<ShipmentsProduct>> groupedByPlanId = shipmentsProducts.stream()
				.collect(Collectors.groupingBy(ShipmentsProduct::getPlanId));

		// 将每个 Plan 对应的 ShipmentsProductList 设置到 Plan 中
		planList.forEach(plan -> {
			// 获取当前 Plan 对应的 ShipmentsProduct 列表
			List<ShipmentsProduct> products = groupedByPlanId.get(plan.getId());

			// 如果有对应的 ShipmentsProduct 列表，给每个产品设置对应的 planNumber
			if (products != null) {
				products.forEach(product -> product.setPlanNumber(plan.getPlanNumber()));
			}

			// 将 ShipmentsProductList 设置到 Plan 中
			plan.setShipmentsProductList(products);
		});
		walmartStockPlan.setPlanList(planList);

		return walmartStockPlan;
	}

	/**
	 * 查询walmart备货计划列表
	 *
	 * @param walmartStockPlan walmart备货计划
	 * @return walmart备货计划
	 */
	@Override
	public List<WalmartStockPlan> selectWalmartStockPlanList(WalmartStockPlan walmartStockPlan) {
		List<String> Ids = null;
		if(walmartStockPlan.getSearchSku() != null && !walmartStockPlan.getSearchSku().isEmpty()){
			// 查询sku关联的明细
			Ids = walmartStockProductDetailsMapper.selectStockIdsBySku(walmartStockPlan.getSearchSku());
			if(Ids.size()<=0){
				return new ArrayList<>();
			}
		}

		// 查询出备货计划列表
		List<WalmartStockPlan> walmartStockPlans = walmartStockPlanMapper.selectWalmartStockPlanList(walmartStockPlan,Ids);


		if (walmartStockPlans.isEmpty()) {
			return new ArrayList<>();
		}
		// 获取所有备货计划的 stockId
		List<String> stockIds = walmartStockPlans.stream().map(WalmartStockPlan::getStockId).collect(Collectors.toList());

		// 查询 stock_id 对应的状态
		List<Map<String, String>> stockStatuses = walmartStockPlanMapper.selectStockStatuses(stockIds);
		// 更新 walmartStockPlans 中的状态
		for (WalmartStockPlan stockPlan : walmartStockPlans) {
			String stockId = stockPlan.getStockId();

			// 使用流的方式查找对应的状态
			stockStatuses.stream()
					.filter(statusEntry -> stockId.equals(statusEntry.get("stock_id"))) // 根据 stockId 找到对应的状态
					.findFirst() // 获取匹配的第一个元素
					.ifPresent(statusEntry -> stockPlan.setReceiptStatus(statusEntry.get("receipt_status"))); // 如果找到，则设置状态
		}

		// 根据 stockIds 批量查询所有备货计划的明细
		List<WalmartStockProductDetails> details = walmartStockProductDetailsMapper.selectList(new LambdaQueryWrapper<>(WalmartStockProductDetails.class)
				.in(WalmartStockProductDetails::getStockId, stockIds));

		// 查询 sku 对应的状态
		List<Map<String, String>> skuStatuses = walmartStockPlanMapper.selectStockSkuStatuses(stockIds);
		// 更新 WalmartStockProductDetails 中的状态
//		for (WalmartStockProductDetails detail : details) {
//			String stockId = detail.getStockId();
//			String sku = detail.getSku();
//		}

		// 获取所有 shipmentsProductId
		List<String> shipmentsProductIds = details.stream()
				.map(WalmartStockProductDetails::getShipmentsProductId)
				.collect(Collectors.toList());

		// 根据 shipmentsProductIds 批量查询所有 ShipmentsProduct 数据
		List<ShipmentsProduct> shipmentsProducts = shipmentsProductMapper.selectListByIds(shipmentsProductIds);


		// 检查是否有数据
		if (shipmentsProducts != null && !shipmentsProducts.isEmpty()) {
			// 提取所有的产品id
			List<Long> productIds = shipmentsProducts.stream()
					.map(ShipmentsProduct::getProductId)
					.filter(Objects::nonNull) // 过滤掉null的productId
					.collect(Collectors.toList());

			// 确保有有效的产品ID再进行查询
			if (!productIds.isEmpty()) {
				Map<Integer, List<String>> collect = productImageMapper.selectProductImagesByProductId(productIds)
						.stream()
						.collect(Collectors.groupingBy(
								ProductImage::getProductId,
								Collectors.mapping(ProductImage::getProductImagePath, Collectors.toList())
						));

				// 将产品图片路径封装到每一个详细中去
				shipmentsProducts.forEach(e -> {
					Integer productId = e.getProductId() != null ? e.getProductId().intValue() : null;
					if (productId != null && collect.containsKey(productId) && !collect.get(productId).isEmpty()) {
						e.setImgName(collect.get(productId));
					} else {
						// 如果没有图片，可以设置一个默认值或者留空
						e.setImgName(null); // 或者设置默认图片路径
					}
				});
			} else {
				// 没有有效的产品ID，直接设置所有记录的图片为null或默认值
				shipmentsProducts.forEach(e -> e.setImgName(null)); // 或者设置默认图片路径
			}
		}

		List<Long> planIds;
		if (shipmentsProducts == null || shipmentsProducts.isEmpty()) {
			planIds = Collections.emptyList();
		} else {
			planIds = shipmentsProducts.stream()
					.map(ShipmentsProduct::getPlanId)
					.filter(Objects::nonNull)
					.collect(Collectors.toList());
		}

		// 批量查询 Plan 数据，如果 planIds 为空，则返回空列表
		List<Plan> planList = planIds.isEmpty() ? Collections.emptyList() : planMapper.selectPlanByIds(planIds);

		// 以 planId 为键构建 Plan 对象的 Map，重复的键保留第一个
		Map<Long, Plan> planMap = planList.stream()
				.collect(Collectors.toMap(Plan::getId, Function.identity(), (existing, replacement) -> existing));

		// ========== 核心逻辑：为每个备货计划独立创建自己的 Plan 副本 ==========
		walmartStockPlans.forEach(stockPlan -> {

			// （1） 找到与当前备货计划相关的所有明细
			List<WalmartStockProductDetails> productDetailsList = details.stream()
					.filter(d -> stockPlan.getStockId().equals(d.getStockId()))
					.collect(Collectors.toList());
			if (productDetailsList.isEmpty()) {
				// 没有明细，跳过
				return;
			}


			// （2） 用一个 Map 来存储该 备货计划 下的 “PlanID -> 该 Plan 对应的 ShipmentsProduct 列表”
			Map<Long, List<ShipmentsProduct>> groupedByPlanId = new HashMap<>();


			// （3） 遍历所有明细，找出对应的 ShipmentsProduct
			productDetailsList.forEach(detail -> {
				shipmentsProducts.stream()
						.filter(sp -> Objects.equals(sp.getId(), detail.getShipmentsProductId()))
						.forEach(sp -> {
//							// 将明细中的 shipmentsProductCount 赋值到 shipmentsProduct 对象中
//							sp.setPlanCount(detail.getShipmentsProductCount()); // 发货数量
//							sp.setFreight(detail.getFreight()); // 运费
//							Long planId = sp.getPlanId();

							// >>> MODIFIED: 深度克隆原始 ShipmentProduct，避免共享可变对象
							ShipmentsProduct spClone = new ShipmentsProduct();
							BeanUtils.copyProperties(sp, spClone);

							// 将明细中的 shipmentsProductCount 赋值到克隆对象中
							spClone.setPlanCount(detail.getShipmentsProductCount()); // 发货数量
							spClone.setFreight(detail.getFreight()); // 运费

							Long planId = spClone.getPlanId();
							// 根据 planId 将 sp 放入对应的List中
							groupedByPlanId
									.computeIfAbsent(planId, k -> new ArrayList<>())
									.add(spClone);
						});
			});

			// （4） 根据 groupedByPlanId 的 Key（planId）找到原始 Plan；并复制成新的 Plan
			List<Plan> relatedPlans = groupedByPlanId.keySet().stream()
					.map(pid -> {
						Plan original = planMap.get(pid);
						if (original == null) {
							return null; // 数据异常或未查到
						}
						// 创建一个新的 Plan 对象，把公共信息复制过来
						Plan newPlan = new Plan();
						BeanUtils.copyProperties(original, newPlan);

						// 给新的 Plan 设置“只属于这个备货计划”的 shipmentsProductList
						List<ShipmentsProduct> spList = groupedByPlanId.get(pid);
						for (ShipmentsProduct shipmentsProduct : spList) {
							// 根据货运id查询对应的货运明细并绑定到对应的备货明细中去
							Map<String, ShipmentItem> skuToShipmentItemMap = shipmentItemMapper
									.selectByShipmentIdAndSku(stockPlan.getShipmentId(), shipmentsProduct.getSku())
									.stream()
									.collect(Collectors.toMap(
											ShipmentItem::getSku,         // Key 提取器
											Function.identity(),          // Value 提取器（对象本身）
											(existing, replacement) -> {
												throw new ServiceException("货运计划中出现重复的SKU:" + existing.getSku());
											} // 冲突处理策略（保留已有条目）
									));

							// 将收货数量进行赋值
							ShipmentItem shipmentItem = skuToShipmentItemMap.get(shipmentsProduct.getSku());
							if (shipmentItem != null) {
								shipmentsProduct.setReceivedQuantity(shipmentItem.getReceivedQty().intValue());
							}

							// 获取产品的包装清单和属性赋值给销售计划明细
							Product product = productMapper.selectAttributeAndPackingList(shipmentsProduct.getProductId());
							if (product != null) {
								shipmentsProduct.setPackingList(product.getPackingList()); // 包装清单
								shipmentsProduct.setAttribute(product.getAttribute()); // 商品属性
							}

							//  获取采购总成本
							List<OutboundStockLog> outboundStockLogs = outboundStockLogMapper.selectList(new LambdaQueryWrapper<OutboundStockLog>()
									.eq(OutboundStockLog::getOutboundOrderId, stockPlan.getStockId()));

							if (outboundStockLogs != null && !outboundStockLogs.isEmpty()) {
								// 获取当前产品对应的出库记录
								List<OutboundStockLog> productStockLogs = outboundStockLogs.stream()
										.filter(e -> Objects.equals(e.getSku(), shipmentsProduct.getSku()))
										.collect(Collectors.toList());

								if (!productStockLogs.isEmpty()) {
									// 总成本：只累加本产品的
									BigDecimal totalQuantity = productStockLogs.stream()
											.map(OutboundStockLog::getDeductionCost)
											.reduce(BigDecimal.ZERO, BigDecimal::add);

									// 批次 ID
									List<Integer> collect = productStockLogs.stream()
											.map(OutboundStockLog::getBatchId)
											.collect(Collectors.toList());

									// 入库记录（只查本产品的批次）
									List<InboundStock> inboundStocks = inboundStockMapper.selectList(
											new LambdaQueryWrapper<InboundStock>()
													.in(InboundStock::getId, collect)
									);

									Map<Integer, InboundStock> collect2 = inboundStocks.stream().collect(Collectors.toMap(
											InboundStock::getId,
											Function.identity(),
											(existing, replacement) -> existing
									));

									Map<Long, ProcuredOrders> collect1 = procuredOrdersMapper.selectByOrdersId(
											inboundStocks.stream().map(InboundStock::getPurchaseOrderId).toArray(Long[]::new)
									).stream().collect(Collectors.toMap(
											ProcuredOrders::getOrdersId,
											Function.identity(),
											(existing, replacement) -> existing
									));

									// 构建采购信息
									StringBuilder financialPurchasingInfoBuilder = new StringBuilder();  // 财务专用
									StringBuilder publicPurchasingInfoBuilder = new StringBuilder();     // 公开信息

									// 给财务看的详细信息
									financialPurchasingInfoBuilder.append("由").append(productStockLogs.size()).append("笔采购入库成本组成:\n");
									publicPurchasingInfoBuilder.append("由").append(productStockLogs.size()).append("笔采购入库成本组成:\n");
									// 给所有人可以看到的采购入库成本


									productStockLogs.forEach(e -> {
										InboundStock inboundStock = collect2.get(e.getBatchId());
										if (inboundStock == null) return;

										ProcuredOrders procuredOrder = collect1.get(inboundStock.getPurchaseOrderId());
										if (procuredOrder == null) return;

										// 财务查看的所有信息拼接
										financialPurchasingInfoBuilder
												.append("\n采购单号：").append(procuredOrder.getOrdersNumber()).append("\n")
												.append("数量*单个采购成本  ->  ").append(e.getDeductedQuantity())
												.append("*").append(inboundStock.getUnitCost().setScale(4, RoundingMode.HALF_UP)).append("\n");

										// 所有人查看的采购成本拼接
										publicPurchasingInfoBuilder.append(inboundStock.getUnitCost().setScale(4, RoundingMode.HALF_UP)).append("\n");
									});

									// 设置结果
									shipmentsProduct.setPurchasingCost(totalQuantity);
									shipmentsProduct.setPurchasingInfo(financialPurchasingInfoBuilder.toString());
									shipmentsProduct.setPublicPurchasingInfoBuilder(publicPurchasingInfoBuilder.toString());
								}
							}

							// 使用流的方式查找对应的状态
							skuStatuses.stream()
									.filter(statusEntry -> shipmentsProduct.getId().equals(statusEntry.get("id")) && shipmentsProduct.getSku().equals(statusEntry.get("sku"))) // 根据 stockId 找到对应的状态
									.findFirst() // 获取匹配的第一个元素
									.ifPresent(statusEntry -> shipmentsProduct.setReceiptStatus(statusEntry.get("receipt_status"))); // 如果找到，则设置状态

						}
						newPlan.setShipmentsProductList(new ArrayList<>(spList));
						return newPlan;
					})
					.filter(Objects::nonNull)
					.collect(Collectors.toList());

			// （5） 将相关的销售计划设置到当前备货计划中
			stockPlan.setPlanList(relatedPlans);
		});
		return walmartStockPlans;
	}


	/**
	 * 新增walmart备货计划
	 *
	 * @param plan walmart备货计划
	 * @return 结果
	 */
	@Transactional
	@Override
	public int insertWalmartStockPlan(Plan plan) {
		// 插入对应的仓库备货记录
		WalmartStockPlan walmartStockPlan = new WalmartStockPlan();
		walmartStockPlan.setStockId(IdUtils.fastSimpleUUID());
		walmartStockPlan.setStockNumber(generateCode(1).get(0)); // 备货单编号
		walmartStockPlan.setShippingMethod(plan.getSelectedManner()); // 货运方式
		walmartStockPlan.setLogisticsCompany(plan.getLogisticsCompany()); // 物流公司
		walmartStockPlan.setStockWarehouse(plan.getWarehouse()); // 备货仓库
		walmartStockPlan.setStatus(0); // 默认为未发货
		walmartStockPlan.setCreateTime(new Date()); // 创建时间
		walmartStockPlan.setCreateBy(SecurityUtils.getNickname()); // 创建人
		walmartStockPlan.setUpdateTime(new Date()); // 修改时间
		walmartStockPlan.setRemark(plan.getRemark()); //备注
		walmartStockPlan.setAuthId(plan.getGoShop().intValue()); // 关联店铺id

		// 插入备货计划
		int result = walmartStockPlanMapper.insertWalmartStockPlan(walmartStockPlan);
		// 插入对应的备货计划明细
		plan.getShipmentsProductList().forEach(
				item -> {
					WalmartStockProductDetails details = new WalmartStockProductDetails();
					details.setStockId(walmartStockPlan.getStockId()); // 备货id
					details.setSku(item.getSku()); // 产品sku
					details.setShipmentsProductId(item.getId()); // 备货产品明细id(销售计划明细产品id)
					details.setFlag(0); // 初始未导入装箱清单
					walmartStockProductDetailsMapper.insert(details); // 插入明细信息
				}
		);
		return result;
	}

	private List<String> generateCode(Integer count) {
		synchronized (this) {  // 使用同步块，防止多线程并发访问
			// 获取当前日期
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
			String currentDate = dateFormat.format(new Date());

			// 获取最后一个收货单编号，在事务中加锁（使用 FOR UPDATE）
			String lastStockNumber = walmartStockPlanMapper.selectLastStockPlanNumberForUpdate(currentDate);

			// 生成的编号列表
			List<String> generatedCodes = new ArrayList<>();

			// 如果没有找到最后一个编号，或者日期不匹配，说明是当天的第一个收货单
			String number;
			if (StringUtils.isEmpty(lastStockNumber) || !lastStockNumber.substring(2, 10).equals(currentDate)) {
				// 如果没有找到或者日期不匹配，生成从BS202411290001开始
				number = Constants.INBOUND_NUMBER;
			} else {
				// 提取编号中的序列部分，并加一
				String lastNumberStr = lastStockNumber.substring(10); // 编号格式：CR + 日期（8位） + 序号（4位）
				long lastNumber = Long.parseLong(lastNumberStr);
				number = String.format("%04d", lastNumber + 1);
			}

			// 生成指定数量的编号
			for (int i = 0; i < count; i++) {
				String code = Constants.STOCK_PLAN_NUMBER + currentDate + number;
				// 检查生成的编号是否已存在，避免并发冲突
				int retryCount = 0;
				while (walmartStockPlanMapper.checkStockPlanNumberExists(code) > 0 && retryCount < 5) {
					// 如果编号已经存在，重新生成
					retryCount++;
					String lastReceiptNumberRetry = walmartStockPlanMapper.selectLastStockPlanNumberForUpdate(currentDate);
					if (StringUtils.isEmpty(lastReceiptNumberRetry) || !lastReceiptNumberRetry.substring(2, 10).equals(currentDate)) {
						number = Constants.INBOUND_NUMBER;
					} else {
						String lastNumberStrRetry = lastReceiptNumberRetry.substring(10);
						long lastNumberRetry = Long.parseLong(lastNumberStrRetry);
						number = String.format("%04d", lastNumberRetry + 1);
					}
					code = Constants.STOCK_PLAN_NUMBER + currentDate + number;
				}

				if (retryCount >= 5) {
					// 达到重试次数限制，可以抛出异常或返回失败标志
					throw new RuntimeException("Failed to generate unique receipt number after 5 retries.");
				}

				// 将生成的编号添加到列表
				generatedCodes.add(code);

				// 更新编号的序号
				long currentNumber = Long.parseLong(number);
				number = String.format("%04d", currentNumber + 1);
			}

			return generatedCodes;
		}
	}

	/**
	 * 修改walmart备货计划
	 *
	 * @param walmartStockPlan walmart备货计划
	 * @return 结果
	 */
	@Transactional
	@Override
	public int updateWalmartStockPlan(WalmartStockPlan walmartStockPlan) {
		// 查询一下最新的备货计划状态看是否已经发货
		List<WalmartStockPlan> walmartStockPlans = walmartStockPlanMapper.selectWalmartStockPlanStatus(new String[]{walmartStockPlan.getStockId()});

		// 只要集合中存在备货状态为已发货的状态就返回true
		boolean hasStatusOne = walmartStockPlans.stream().anyMatch(plan -> plan.getStatus() == 1);

		if (hasStatusOne) {
			throw new ServiceException("备货计划已经发货，禁止修改!");
		}

		// 修改备货计划
		walmartStockPlan.setUpdateTime(DateUtils.getNowDate());
		walmartStockPlan.setUpdateBy(SecurityUtils.getNickname());
		int result = walmartStockPlanMapper.updateWalmartStockPlan(walmartStockPlan);

		// 删除关联的销售计划明细
		String stockId = walmartStockPlan.getStockId();
		walmartStockProductDetailsMapper.delete(new LambdaQueryWrapper<>(WalmartStockProductDetails.class)
				.eq(WalmartStockProductDetails::getStockId, stockId));

		List<WalmartStockProductDetails> list = new ArrayList<>();
		// 重新插入对应的明细
		walmartStockPlan.getSelectedPlans().forEach(
				item -> {
					WalmartStockProductDetails walmartStockProductDetails = new WalmartStockProductDetails();
					walmartStockProductDetails.setStockId(stockId); // 备货计划id
					walmartStockProductDetails.setShipmentsProductId(item.getId()); // 明细id
					walmartStockProductDetails.setSku(item.getSku()); // sku
					System.out.println(item.getSku());
					walmartStockProductDetails.setFlag(Constants.NOT_DELETED_STATUS);  // 初始化为未导入对应的备货清单
					list.add(walmartStockProductDetails);
				}
		);

		// 1.1 每次导入前将之前可能导入过的数据进行清空
		walmartStockProductDetailsMapper.update(new WalmartStockProductDetails(), new LambdaUpdateWrapper<>(WalmartStockProductDetails.class)
				.set(WalmartStockProductDetails::getShipmentsProductCount, 0) // 清空导入数量
				.set(WalmartStockProductDetails::getFlag, Constants.NOT_DELETED_STATUS) // 代表未导入数据
				.set(WalmartStockProductDetails::getFreight, Constants.NOT_DELETED_STATUS) // 清空运费
				.eq(WalmartStockProductDetails::getStockId, stockId));

		// 1.2 清空当前备货计划的导入数据
		walmartStockPlanPackingListMapper.delete(new LambdaQueryWrapper<>(WalmartStockPlanPackingList.class)
				.eq(WalmartStockPlanPackingList::getStockId, stockId));

		// 1.3 清空对当前备货计划明细的装箱关联数据
		packingDetailMapper.delete(new LambdaQueryWrapper<>(WalmartStockPlanPackingDetail.class)
				.eq(WalmartStockPlanPackingDetail::getStockId, stockId));

		walmartStockProductDetailsMapper.insertBatch(list); // 批量插入对应的关联记录
		return result;
	}

	/**
	 * 批量删除walmart备货计划
	 *
	 * @param stockIds 需要删除的walmart备货计划主键
	 * @return 结果
	 */
	@Transactional
	@Override
	public int deleteWalmartStockPlanByStockIds(String[] stockIds) {
		// 查询备货计划的状态
		List<WalmartStockPlan> walmartStockPlans = walmartStockPlanMapper.selectWalmartStockPlanStatus(stockIds);

		// 只要集合中存在备货状态为已发货的状态就返回true
		boolean hasStatusOne = walmartStockPlans.stream().anyMatch(plan -> plan.getStatus() == 1);

		if (hasStatusOne) {
			throw new ServiceException("删除的备货计划中存在已经发货的备货计划！");
		}

		// 删除对应的备货计划关联明细
		walmartStockProductDetailsMapper.delete(new LambdaQueryWrapper<>(WalmartStockProductDetails.class)
				.in(WalmartStockProductDetails::getStockId, Arrays.asList(stockIds)));

		// 删除对应的导入清单
		walmartStockPlanPackingListMapper.delete(new LambdaQueryWrapper<>(WalmartStockPlanPackingList.class)
				.in(WalmartStockPlanPackingList::getStockId, Arrays.asList(stockIds)));

		// 清空对当前备货计划明细的装箱关联数据
		packingDetailMapper.delete(new LambdaQueryWrapper<>(WalmartStockPlanPackingDetail.class)
				.in(WalmartStockPlanPackingDetail::getStockId, Arrays.asList(stockIds)));
		// 删除备货计划
		return walmartStockPlanMapper.deleteWalmartStockPlanByStockIds(stockIds);
	}

	/**
	 * 删除walmart备货计划信息
	 *
	 * @param stockId walmart备货计划主键
	 * @return 结果
	 */
	@Override
	public int deleteWalmartStockPlanByStockId(String stockId) {
		return walmartStockPlanMapper.deleteWalmartStockPlanByStockId(stockId);
	}

	/**
	 * 导入运费
	 *
	 * @param planPackingLists
	 * @return
	 */
	@Transactional
//	@Override
	public int importFreight(List<WalmartStockPlanPackingList> planPackingLists, BigDecimal totalAmount) {
		ObjectMapper mapper = new ObjectMapper();
		// 如果想要格式化输出（换行、缩进），可以开启下面这一行
		mapper.enable(SerializationFeature.INDENT_OUTPUT);
		try {
			String json = mapper.writeValueAsString(planPackingLists);
			System.out.println(json);
		} catch (Exception e) {
			e.printStackTrace();
		}
		int resultCount = 0;
		// 遍历每一条导入的运费清单记录
		for (WalmartStockPlanPackingList packingRow : planPackingLists) {
			// 取得总运费和箱内总数量
			BigDecimal totalFreight = packingRow.getFreight();
			int totalQuantity = packingRow.getQuantity();
			if (totalFreight == null || totalQuantity <= 0) {
				continue; // 数据不合法则跳过
			}
			// 计算每单位运费
			BigDecimal perUnitFreight = totalFreight.divide(BigDecimal.valueOf(totalQuantity), 6, RoundingMode.UP);

			// 查询该记录对应的装箱明细（ stockId、sku、gtin 和 boxNumber 能唯一定位到这些明细）
			List<WalmartStockPlanPackingDetail> detailList = packingDetailMapper.selectList(
					new LambdaQueryWrapper<WalmartStockPlanPackingDetail>()
							.eq(WalmartStockPlanPackingDetail::getStockId, packingRow.getStockId())
							.eq(WalmartStockPlanPackingDetail::getSku, packingRow.getSku())
							.eq(WalmartStockPlanPackingDetail::getBoxNumber, packingRow.getBoxNumber())
							.eq(WalmartStockPlanPackingDetail::getGtin, packingRow.getGtin())
			);

			if (detailList.isEmpty()) {
				throw new ServiceException("装箱明细不存在，请刷新页面重试");
			}
			// 遍历所有查询到的明细记录，更新其运费
			for (WalmartStockPlanPackingDetail detail : detailList) {
				int allocatedQty = detail.getAllocatedQuantity();
				// 分配给该明细的运费 = 每单位运费 * 分配数量
				BigDecimal allocatedFreight = perUnitFreight.multiply(BigDecimal.valueOf(allocatedQty));
				detail.setAllocatedFreight(allocatedFreight);

				// 更新装箱明细的运费字段
				resultCount += packingDetailMapper.updateById(detail);

				// 同时更新销售计划明细表中的运费字段，将该明细分配的运费累加进去
				// 这里使用 SQL 方式累加： freight = IFNULL(freight, 0) + allocatedFreight
				int updateResult = walmartStockProductDetailsMapper.update(new WalmartStockProductDetails(),
						new LambdaUpdateWrapper<WalmartStockProductDetails>()
								.setSql("freight = IFNULL(freight, 0) + " + allocatedFreight.toPlainString())
								.eq(WalmartStockProductDetails::getStockId, detail.getStockId())
								.eq(WalmartStockProductDetails::getShipmentsProductId, detail.getShipmentsProductId())
								.eq(WalmartStockProductDetails::getSku, detail.getSku())
				);
				resultCount += updateResult;
			}
			walmartStockPlanPackingListMapper.updateById(packingRow);
		}

		if (true) {
			throw new ServiceException("");
		}

		return resultCount;
	}


	/**
	 * 辅助方法：“尾数进一”到小数点后两位 (向上取整到两位小数)
	 */
	private BigDecimal ceilToTwoDecimals(BigDecimal num) {
		if (num == null) {
			return BigDecimal.ZERO.setScale(2);
		}
		return num.setScale(2, RoundingMode.CEILING);
	}

	private BigDecimal parseDimension(String dimStr) {
		if (dimStr == null || dimStr.trim().isEmpty()) {
			// 或者抛出异常，取决于您的错误处理策略
			throw new IllegalArgumentException("尺寸字符串不能为空: " + dimStr);
		}
		return new BigDecimal(dimStr.trim());
	}

	private BigDecimal parseWeightString(String weightStr) {
		if (weightStr == null || weightStr.isEmpty()) {
			return BigDecimal.ZERO;
		}
		return new BigDecimal(weightStr.replaceAll("(?i)KG", "").trim());
	}

	/**
	 * 退货退款审核通过，生成出库单
	 */
	@Override
	@Transactional
	public void saveWalmartStockPlanToPaymentReturn(List<PaymentReturnDto> items, String paymentType) {
		if (items == null || items.isEmpty()) {
			throw new IllegalArgumentException("支付退货列表不能为空");
		}
		String procuredOrderId = items.get(0).getProcuredOderId();
		ProcuredStorageDto dto  = procuredStorageApi.selectWarehouseIdByOrdersId(Long.valueOf(procuredOrderId));
		if(dto == null){
			throw new ServiceException("获取采购入库单对象失败");
		}
		ProcuredOrders procuredOrders = procuredOrdersMapper.selectProcuredOrdersByOrdersId(Long.valueOf(procuredOrderId));
		if (procuredOrders == null ) {
			throw new ServiceException("未查询到对应的采购计划，请检查！");
		}

		// 总出库量
		AtomicInteger totalQuantity = new AtomicInteger(0);
		// 总出库成本
		AtomicReference<BigDecimal> totalAmount = new AtomicReference<>(BigDecimal.ZERO);

		List<OutboundOrderDetail> detailList = new ArrayList<>();
		items.forEach(item -> {
			String sku = item.getSku();
			Integer available = item.getQuantity();

			LambdaQueryWrapper<InboundStock> lambdaQuery = new LambdaQueryWrapper<>();
			lambdaQuery.eq(InboundStock::getSku, sku)
					.eq(InboundStock::getWarehouseId, dto.getWarehouseId())
					.eq(InboundStock::getPurchaseOrderId, procuredOrderId);

			InboundStock is = inboundStockMapper.selectOne(lambdaQuery);
			if(is == null){
				throw new ServiceException(sku+"未查询到对应入库记录，请检查！");
			}
			BigDecimal unitCost = is.getUnitCost();// 单品成本

			// 累积出库量
			totalQuantity.addAndGet(available);

			// 累积出库成本 = 数量 * 单品成本
			BigDecimal itemCost = unitCost.multiply(new BigDecimal(available));
			totalAmount.set(totalAmount.get().add(itemCost));

			OutboundOrderDetail detail = new OutboundOrderDetail();
			detail.setProductId(Long.valueOf(item.getProductId())); // 产品id
			detail.setSku(item.getSku()); // sku
			detail.setQuantity(available); // 当前sku的扣减数量
			detail.setAmount(itemCost); // 扣减总成本
			detailList.add(detail);
		});

		Integer setOutboundType = Constants.OUTBOUND_TYPE_RETURN;
		//paymentType：申请方式（0预付款，1应付款，2仅退款，3退货退款，4换货）
		if(paymentType.equals("4")){
			setOutboundType = Constants.OUTBOUND_TYPE_EXCHANGE;
		}
		// 创建对应的出库单
		OutboundOrder outboundOrder = new OutboundOrder();
		outboundOrder.setOutboundNo(generateCodeInbound(1).get(0)); // 出库单号
		outboundOrder.setWarehouseId(Math.toIntExact(dto.getWarehouseId())); // 仓库id
		outboundOrder.setOutboundType(setOutboundType); // 出库类型
		outboundOrder.setStatus(0); // 出库状态为未出库
		outboundOrder.setRelatedOrderNo(procuredOrders.getOrdersNumber()); // 关联备货单据号
		outboundOrder.setTotalQuantity(totalQuantity.get()); // 出库总数量
		outboundOrder.setTotalAmount(totalAmount.get()); // 出库总成本
		outboundOrder.setCreator(SecurityUtils.getNickname()); // 创建人
		outboundOrder.setCreateTime(new Date()); // 创建时间
		outboundOrderMapper.insertOutboundOrder(outboundOrder);
		Long outboundId = outboundOrder.getOutboundId(); // 出库单id

		// 设置出库单id到详细中
		detailList.forEach(
				b -> b.setOutboundId(outboundId)
		);
		// 批量插入出库单详细
		outboundOrderDetailMapper.insertBatch(detailList);

	}
	// --- 结束辅助方法 ---

	// 临时的箱子处理数据结构
	private static class BoxProcessingData {
		String boxNumber;
		List<WalmartStockPlanPackingList> skusInBox = new ArrayList<>();
		String boxSize;
		String boxWeightStr; // 物理箱的原始重量字符串
		BigDecimal physicalBoxChargeableMetric = BigDecimal.ZERO; // 物理箱的计费指标 (KG或M³)
		BigDecimal allocatedBoxFreight = BigDecimal.ZERO; // 分配给这个物理箱的总运费
		BigDecimal totalProductLevelChargeableInBox = BigDecimal.ZERO; // 箱内所有SKU产品级别计费指标的总和

		public BoxProcessingData(String boxNumber, String boxSize, String boxWeightStr) {
			this.boxNumber = boxNumber;
			this.boxSize = boxSize;
			this.boxWeightStr = boxWeightStr;
		}
	}

	@Transactional
	@Override // 如果这是接口实现
	public int importFreight(List<WalmartStockPlanPackingList> planPackingLists,
							 BigDecimal totalOverallFreightAmount, // 这是总运费金额
							 int calculationMode // 1 代表 KG 模式, 2 代表 M³ 模式
	) {
		// 获取备货计划id
		String stockId = planPackingLists.get(0).getStockId();

		// 每次导入之前，先把之前导入的运费进行清空
		walmartStockPlanPackingListMapper.update(null, new LambdaUpdateWrapper<WalmartStockPlanPackingList>()
				.set(WalmartStockPlanPackingList::getFreight, BigDecimal.ZERO)
				.set(WalmartStockPlanPackingList::getUnitFreight, BigDecimal.ZERO)
				.eq(WalmartStockPlanPackingList::getStockId, stockId)
		);

		walmartStockProductDetailsMapper.update(null, new LambdaUpdateWrapper<WalmartStockProductDetails>()
				.set(WalmartStockProductDetails::getFreight, BigDecimal.ZERO)
				.eq(WalmartStockProductDetails::getStockId, stockId)
		);

		packingDetailMapper.update(null, new LambdaUpdateWrapper<WalmartStockPlanPackingDetail>()
				.set(WalmartStockPlanPackingDetail::getAllocatedFreight, BigDecimal.ZERO)
				.eq(WalmartStockPlanPackingDetail::getStockId, stockId)
		);
		ObjectMapper objectMapperForDebug = new ObjectMapper();
		objectMapperForDebug.enable(SerializationFeature.INDENT_OUTPUT);

		if (planPackingLists.isEmpty()) {
			System.out.println("运费清单为空，不进行处理。");
			return 0;
		}
		if (totalOverallFreightAmount == null || totalOverallFreightAmount.compareTo(BigDecimal.ZERO) <= 0) {
			System.out.println("总运费金额不正确 (" + totalOverallFreightAmount + ")，将所有运费设置为0。");
			for (WalmartStockPlanPackingList packingRow : planPackingLists) {
				packingRow.setFreight(BigDecimal.ZERO.setScale(2)); // SKU行项目总运费
				packingRow.setUnitFreight(BigDecimal.ZERO.setScale(2)); // SKU单件运费
			}
			// 即使运费为0，仍可能需要执行后续的数据库更新逻辑（如果业务要求如此）
			// 这里我们先计算，如果后续逻辑依赖非0运费，会有问题
		} else {
			// === 开始新的运费计算逻辑 ===

			// 步骤 1: 按 boxNumber 分组 SKU，并收集初始箱子属性
			Map<String, BoxProcessingData> boxesMap = new HashMap<>();
			for (WalmartStockPlanPackingList skuRow : planPackingLists) {
				// 确保 skuRow 有 getBoxNumber, getBoxSize, getBoxWeight 方法
				boxesMap.computeIfAbsent(skuRow.getBoxNumber(),
								k -> new BoxProcessingData(skuRow.getBoxNumber(), skuRow.getBoxSize(), skuRow.getBoxWeight()))
						.skusInBox.add(skuRow);
				// 清除或初始化行项目上的运费字段，因为它们将被重新计算
				// 这些setFreight和setUnitFreight是指 WalmartStockPlanPackingList 类的字段
				skuRow.setFreight(null);
				skuRow.setUnitFreight(null);
			}

			BigDecimal overallTotalChargeableMetric = BigDecimal.ZERO;

			// 步骤 2: 计算每个物理箱的计费指标 (重量或体积)
			for (BoxProcessingData boxData : boxesMap.values()) {
				String[] boxDimsStr = boxData.boxSize.split("\\*");
				BigDecimal l = parseDimension(boxDimsStr[0]);
				BigDecimal w = parseDimension(boxDimsStr[1]);
				BigDecimal h = parseDimension(boxDimsStr[2]);
				BigDecimal actualBoxWeightKg = parseWeightString(boxData.boxWeightStr);

				BigDecimal physicalChargeable;
				if (calculationMode == 1) { // KG 模式计算物理箱
					BigDecimal volumeWeight = l.multiply(w).multiply(h).divide(KG_DIVISOR, CALCULATION_SCALE, RoundingMode.UP);
					physicalChargeable = ceilToTwoDecimals(volumeWeight.max(actualBoxWeightKg));
				} else if (calculationMode == 2) { // M³ 模式计算物理箱
					BigDecimal calculatedVolumeM3 = ceilToTwoDecimals(
							l.multiply(w).multiply(h).divide(M3_DIVISOR, CALCULATION_SCALE, RoundingMode.UP)
					);
					BigDecimal actualWeightEquivalentM3 = ceilToTwoDecimals(
							actualBoxWeightKg.divide(M3_WEIGHT_CONVERSION_FACTOR, CALCULATION_SCALE, RoundingMode.UP)
					);
					physicalChargeable = calculatedVolumeM3.max(actualWeightEquivalentM3);
				} else {
					throw new ServiceException("未知的计算模式: " + calculationMode);
				}
				boxData.physicalBoxChargeableMetric = physicalChargeable;
				overallTotalChargeableMetric = overallTotalChargeableMetric.add(physicalChargeable);
			}

			if (overallTotalChargeableMetric.compareTo(BigDecimal.ZERO) <= 0) {
				System.out.println("总计费重量/体积为0，无法分摊运费。所有运费将设置为0。");
				for (WalmartStockPlanPackingList packingRow : planPackingLists) {
					packingRow.setFreight(BigDecimal.ZERO.setScale(2));
					packingRow.setUnitFreight(BigDecimal.ZERO.setScale(2));
				}
			} else {
				// 步骤 3: 将总运费分配给每个物理箱
				for (BoxProcessingData boxData : boxesMap.values()) {
					BigDecimal boxShareRatio = boxData.physicalBoxChargeableMetric.divide(overallTotalChargeableMetric, CALCULATION_SCALE * 2, RoundingMode.UP);
					BigDecimal rawBoxFreight = totalOverallFreightAmount.multiply(boxShareRatio);
					if (calculationMode == 1) { // KG 模式 - 箱子运费四舍五入到整数
						boxData.allocatedBoxFreight = rawBoxFreight.setScale(4, RoundingMode.UP);
					} else if (calculationMode == 2) { // M³ 模式 - 箱子运费“尾数进一”到两位小数
						boxData.allocatedBoxFreight = ceilToTwoDecimals(rawBoxFreight);
					}
				}

				// 步骤 4 & 5: 计算产品级别的计费指标，并将箱子运费分配给箱内SKU
				for (BoxProcessingData boxData : boxesMap.values()) {
					// 箱内第一遍循环: 计算产品级别的计费指标 (按SKU行项目)
					for (WalmartStockPlanPackingList skuRow : boxData.skusInBox) {
						String[] productDimsStr = skuRow.getProductSize().split("\\*");
						BigDecimal pl = parseDimension(productDimsStr[0]);
						BigDecimal pw = parseDimension(productDimsStr[1]);
						BigDecimal ph = parseDimension(productDimsStr[2]);
						BigDecimal productUnitWeightKg = parseWeightString(skuRow.getProductWeight());
						BigDecimal quantity = new BigDecimal(skuRow.getQuantity());

						BigDecimal productLevelTotalChargeableMetric;
						if (calculationMode == 1) { // KG 模式计算产品行
							BigDecimal productTotalVolumeWeight = pl.multiply(pw).multiply(ph).multiply(quantity)
									.divide(KG_DIVISOR, CALCULATION_SCALE, RoundingMode.UP);
							BigDecimal productTotalActualWeight = productUnitWeightKg.multiply(quantity);
							productLevelTotalChargeableMetric = ceilToTwoDecimals(productTotalVolumeWeight.max(productTotalActualWeight));
						} else { // M³ 模式计算产品行
							BigDecimal productTotalVolumeM3 = ceilToTwoDecimals(
									pl.multiply(pw).multiply(ph).multiply(quantity)
											.divide(M3_DIVISOR, CALCULATION_SCALE, RoundingMode.UP)
							);
							BigDecimal productTotalActualWeightEquivalentM3 = ceilToTwoDecimals(
									productUnitWeightKg.multiply(quantity)
											.divide(M3_WEIGHT_CONVERSION_FACTOR, CALCULATION_SCALE, RoundingMode.UP)
							);
							productLevelTotalChargeableMetric = productTotalVolumeM3.max(productTotalActualWeightEquivalentM3);
						}
						skuRow.setTempProductLevelChargeableMetric(productLevelTotalChargeableMetric); // 假设您已在 WalmartStockPlanPackingList 添加此临时字段
						boxData.totalProductLevelChargeableInBox = boxData.totalProductLevelChargeableInBox.add(productLevelTotalChargeableMetric);
					}

					// 箱内第二遍循环: 将分配给箱子的运费分摊到SKU
					for (WalmartStockPlanPackingList skuRow : boxData.skusInBox) {
						BigDecimal skuFreightPortion = BigDecimal.ZERO; // 这是此SKU行项目应分摊的总运费部分
						BigDecimal tempProductMetric = skuRow.getTempProductLevelChargeableMetric();

						if (boxData.totalProductLevelChargeableInBox.compareTo(BigDecimal.ZERO) > 0) {
							BigDecimal skuShareRatioInBox = tempProductMetric.divide(boxData.totalProductLevelChargeableInBox, CALCULATION_SCALE * 2, RoundingMode.UP);
							skuFreightPortion = boxData.allocatedBoxFreight.multiply(skuShareRatioInBox);
						} else if (!boxData.skusInBox.isEmpty() && boxData.allocatedBoxFreight.compareTo(BigDecimal.ZERO) != 0) {
							skuFreightPortion = boxData.allocatedBoxFreight.divide(new BigDecimal(boxData.skusInBox.size()), CALCULATION_SCALE, RoundingMode.UP);
						}

						// 根据您的手动示例 (如CC2078: freight=23.73元)，这里对SKU行项目总运费部分进行“尾数进一”
						skuRow.setFreight(ceilToTwoDecimals(skuFreightPortion));

						BigDecimal perUnitFreight = BigDecimal.ZERO.setScale(2);
						if (skuRow.getQuantity() > 0) {
							// 单件运费 = SKU行项目总运费 / 数量，然后“尾数进一”
							// (注意：这里用的是已经经过一次ceil的skuRow.getFreight())
							// 或者更精确：用 skuFreightPortion / quantity, 然后 ceil
							BigDecimal preciseUnitFreight = skuFreightPortion.divide(new BigDecimal(skuRow.getQuantity()), CALCULATION_SCALE, RoundingMode.UP);
							perUnitFreight = ceilToTwoDecimals(preciseUnitFreight);
						}
						skuRow.setUnitFreight(perUnitFreight);
					}
				}
			}
		}
		// === 结束新的运费计算逻辑 ===
		// 此刻, planPackingLists 中的每个对象的 freight 和 unitFreight 字段已被重新计算和填充


		// 添加校验：确保单个运费×数量的总和 >= 原始总运费，且不超过原始总运费+2%
		BigDecimal calculatedTotalFreight = BigDecimal.ZERO;
		for (WalmartStockPlanPackingList packingRow : planPackingLists) {
			BigDecimal unitFreight = packingRow.getUnitFreight();
			int quantity = packingRow.getQuantity();
			if (unitFreight != null && quantity > 0) {
				calculatedTotalFreight = calculatedTotalFreight.add(
						unitFreight.multiply(new BigDecimal(quantity))
				);
			}
		}

		// 计算上界限制
		if (totalOverallFreightAmount != null && totalOverallFreightAmount.compareTo(BigDecimal.ZERO) > 0) {
			BigDecimal upperLimit = totalOverallFreightAmount.multiply(new BigDecimal("1.02"));

			if (calculatedTotalFreight.compareTo(totalOverallFreightAmount) < 0) {
				throw new ServiceException("运费分摊异常：计算后总运费(" + calculatedTotalFreight +
						")小于原始总运费(" + totalOverallFreightAmount + ")");
			} else if (calculatedTotalFreight.compareTo(upperLimit) > 0) {
				BigDecimal excess = calculatedTotalFreight.subtract(upperLimit);
				BigDecimal excessPercentage = excess.multiply(new BigDecimal("100"))
						.divide(totalOverallFreightAmount, 2, RoundingMode.HALF_UP);

				String freightValidationLog = "运费分摊超额：计算后总运费(" + calculatedTotalFreight +
						")超过原始总运费(" + totalOverallFreightAmount + ")的2%上限。" +
						"超出金额：" + excess + "元，" +
						"超出比例：" + excessPercentage + "%，" +
						"最大允许值：" + upperLimit + "元";
				walmartStockPlanMapper.updateFreightValidationLog(freightValidationLog,stockId);
			}
		} else {
			throw new ServiceException("总运费为空，请检查!");
		}

		// --- 开始您原有的数据库更新逻辑 ---
		try {
			String jsonOutput = objectMapperForDebug.writeValueAsString(planPackingLists);
			System.out.println("后端计算后的 planPackingLists 用于数据库更新:");
			System.out.println(jsonOutput);
		} catch (Exception e) {
			// e.printStackTrace(); // 在生产中，使用日志框架
			System.err.println("JSON序列化调试信息时出错: " + e.getMessage());
		}

		int resultCount = 0;
		for (WalmartStockPlanPackingList packingRow : planPackingLists) {
			// 现在使用重新计算过的 packingRow.getFreight() 和 packingRow.getUnitFreight()
			BigDecimal skuLineTotalFreight = packingRow.getFreight(); // 这是此SKU行项目的总运费（已计算）
			BigDecimal perUnitFreightForDb = packingRow.getUnitFreight(); // 这是计算后的单件运费
			int totalQuantity = packingRow.getQuantity();

			// 检查是否有有效的单件运费和数量进行后续处理
			// 注意：如果总运费为0，则单件运费也会是0，这可能是有效的。
			if (perUnitFreightForDb == null || totalQuantity <= 0) {
				// 之前如果 overallTotalChargeableMetric 为0，已经将 freight 和 unitFreight 设为0
				// 但如果数量为0，则不应继续
				if (totalQuantity <= 0) {
					throw new ServiceException("SKU: " + packingRow.getSku() + " 在箱号 " + packingRow.getBoxNumber() + " 中数量为0或无效，跳过数据库更新。");
				}
			}

			// 查询该记录对应的装箱明细
			List<WalmartStockPlanPackingDetail> detailList = packingDetailMapper.selectList(
					new LambdaQueryWrapper<WalmartStockPlanPackingDetail>()
							.eq(WalmartStockPlanPackingDetail::getStockId, packingRow.getStockId())
							.eq(WalmartStockPlanPackingDetail::getSku, packingRow.getSku())
							.eq(WalmartStockPlanPackingDetail::getBoxNumber, packingRow.getBoxNumber())
							.eq(WalmartStockPlanPackingDetail::getGtin, packingRow.getGtin())
			);

			if (detailList.isEmpty()) {
				// 根据业务需求决定是否抛出异常或仅记录日志
				System.err.println("警告: SKU " + packingRow.getSku() + " 在箱号 " + packingRow.getBoxNumber() + " (GTIN: " + packingRow.getGtin() + ") 中未找到装箱明细。");
				throw new ServiceException("装箱明细不存在，请刷新页面重试");
			}

			for (WalmartStockPlanPackingDetail detail : detailList) {
				int allocatedQty = detail.getAllocatedQuantity();
				if (allocatedQty <= 0) continue;

				// 分配给该明细的运费 = (重新计算的)单件运费 * 分配数量
				// 确保 allocatedFreightForDetail 的舍入规则符合财务要求
				BigDecimal allocatedFreightForDetail = perUnitFreightForDb.multiply(BigDecimal.valueOf(allocatedQty))
						.setScale(2, RoundingMode.UP);
				detail.setAllocatedFreight(allocatedFreightForDetail);

				// 更新装箱明细的运费字段
				resultCount += packingDetailMapper.updateById(detail); // 假设 updateById 返回影响的行数

				// 同时更新销售计划明细表中的运费字段
				int updateProductDetailsResult = walmartStockProductDetailsMapper.update(null, // new WalmartStockProductDetails() if needed for some ORMs, else null for setSql
						new LambdaUpdateWrapper<WalmartStockProductDetails>()
								.setSql("freight = IFNULL(freight, 0) + " + allocatedFreightForDetail.toPlainString())
								.eq(WalmartStockProductDetails::getStockId, detail.getStockId())
								.eq(WalmartStockProductDetails::getShipmentsProductId, detail.getShipmentsProductId())
								.eq(WalmartStockProductDetails::getSku, detail.getSku())
				);
				if (updateProductDetailsResult > 0) {
					resultCount += updateProductDetailsResult; // 或者只在成功时累加1，避免重复计数行数
				} else {
					System.err.println("警告: WalmartStockProductDetails 未更新对于 SKU: " + detail.getSku() +
							", ShipmentsProductId: " + detail.getShipmentsProductId() +
							", 分配运费: " + allocatedFreightForDetail.toPlainString());
				}
			}

			walmartStockPlanPackingListMapper.updateById(packingRow);
		}
//		if (true) {
//			throw new ServiceException("");
//		}
		return resultCount;
	}


	/**
	 * 取消关联的备货计划的发货 shipmentId
	 *
	 * @param shipmentId
	 * @return
	 */
	@Override
	public int updateWalmartStockPlanShipmentId(String shipmentId) {
		return walmartStockPlanMapper.updateWalmartStockPlanShipmentId(shipmentId);
	}

	/**
	 * 查询指定 authId 下所有非空的 shipment_id（去重）
	 */
	@Override
	public List<String> selectDistinctShipmentIdsByAuthId(Long authId) {
		return walmartStockPlanMapper.selectDistinctShipmentIdsByAuthId(authId);
	}

	/**
	 * 已取消的walmart入库单，清空备货计划的shipment_id
	 */
	@Override
	public int clearShipmentIdBatch(List<String> cancelledIds) {
		return walmartStockPlanMapper.clearWalmartStockPlanShipmentIdBatch(cancelledIds);
	}


	public List<WalmartStockPlanPackingList> expandBoxNumberRanges(List<WalmartStockPlanPackingList> originalList) {
		List<WalmartStockPlanPackingList> expandedList = new ArrayList<>();
		Pattern rangePattern = Pattern.compile("^(\\d+)-(\\d+)$");

		for (WalmartStockPlanPackingList item : originalList) {
			String boxNumber = item.getBoxNumber();
			Matcher matcher = rangePattern.matcher(boxNumber);

			// 检查是否是范围格式 (例如 "1-5")
			if (matcher.matches()) {
				int start = Integer.parseInt(matcher.group(1));
				int end = Integer.parseInt(matcher.group(2));

				// 确保开始值小于结束值
				if (start <= end) {
					// 展开范围内的每个箱号
					for (int i = start; i <= end; i++) {
						WalmartStockPlanPackingList newItem = cloneItem(item);
						newItem.setBoxNumber(String.valueOf(i));
						expandedList.add(newItem);
					}
				} else {
					// 如果范围无效，保留原始项
					expandedList.add(item);
				}
			} else {
				// 不是范围格式，直接添加
				expandedList.add(item);
			}
		}

		return expandedList;
	}

	// 克隆对象的辅助方法
	private WalmartStockPlanPackingList cloneItem(WalmartStockPlanPackingList original) {
		WalmartStockPlanPackingList clone = new WalmartStockPlanPackingList();
		clone.setId(original.getId());
		clone.setStockId(original.getStockId());
		clone.setSku(original.getSku());
		clone.setGtin(original.getGtin());
		clone.setQuantity(original.getQuantity());
		clone.setBoxSize(original.getBoxSize());
		clone.setBoxWeight(original.getBoxWeight());
		clone.setProductSize(original.getProductSize());
		clone.setProductWeight(original.getProductWeight());
		clone.setFreight(original.getFreight());
		clone.setUnitFreight(original.getUnitFreight());
		clone.setCreatedAt(original.getCreatedAt());
		clone.setTempProductLevelChargeableMetric(original.getTempProductLevelChargeableMetric());
		return clone;
	}


	/**
	 * 导入装箱清单
	 *
	 * @param expandedList
	 * @return
	 * @throws IOException
	 */
	@Transactional
	@Override
	public int importExcel(List<WalmartStockPlanPackingList> expandedList ) {

		List<WalmartStockPlanPackingList> stockPlanEntities = expandBoxNumberRanges(expandedList);

		String stockId = stockPlanEntities.get(0).getStockId();


		ObjectMapper mapper = new ObjectMapper();
		String json = null;
		try {
			json = mapper.writeValueAsString(stockPlanEntities);
		} catch (JsonProcessingException e) {
			throw new RuntimeException(e);
		}
		System.out.println(json);

		// 1.1 每次导入前将之前可能导入过的数据进行清空
		walmartStockProductDetailsMapper.update(new WalmartStockProductDetails(), new LambdaUpdateWrapper<>(WalmartStockProductDetails.class)
				.set(WalmartStockProductDetails::getShipmentsProductCount, 0) // 清空导入数量
				.set(WalmartStockProductDetails::getFlag, Constants.NOT_DELETED_STATUS) // 代表未导入数据
				.set(WalmartStockProductDetails::getFreight, Constants.NOT_DELETED_STATUS) // 清空运费
				.eq(WalmartStockProductDetails::getStockId, stockId));

		// 1.2 清空当前备货计划的导入数据
		walmartStockPlanPackingListMapper.delete(new LambdaQueryWrapper<>(WalmartStockPlanPackingList.class)
				.eq(WalmartStockPlanPackingList::getStockId, stockId));

		// 1.3 清空对当前备货计划明细的装箱关联数据
		packingDetailMapper.delete(new LambdaQueryWrapper<>(WalmartStockPlanPackingDetail.class)
				.eq(WalmartStockPlanPackingDetail::getStockId, stockId));

		// 校验备货计划中是否存在清单中的sku
		// 查询备货计划明细并收集sku
		List<WalmartStockProductDetails> list = walmartStockProductDetailsMapper.selectList(new LambdaQueryWrapper<>(WalmartStockProductDetails.class)
				.eq(WalmartStockProductDetails::getStockId, stockId));

		// 拿到本次导入的销售计划明细id
		List<String> shipmentsProductIds = list.stream().map(WalmartStockProductDetails::getShipmentsProductId).collect(Collectors.toList());

		// 查询本次导入的销售计划明细
		List<ShipmentsProduct> shipmentsProducts = shipmentsProductMapper.selectListByIds(shipmentsProductIds);

		// 查看导入的sku是否存在于备货计划中
		// 4. 将 SKU 列表转换为 Set 以加速查找
		Set<String> skuSet = shipmentsProducts.stream()
				.map(item -> item.getSku() + item.getGtin())
				.collect(Collectors.toSet());

		// 5. 遍历 `stockPlanEntities`，检查 SKU 是否存在于 `skuSet`
		for (WalmartStockPlanPackingList entity : stockPlanEntities) {
			if (!skuSet.contains(entity.getSku() + entity.getGtin())) {
				throw new ServiceException("备货清单中的SKU: " + entity.getSku() + "GTIN:" + entity.getGtin() + " 不存在于备货计划中！");
			}
		}

		// 查询备货计划信息
		WalmartStockPlan walmartStockPlan = walmartStockPlanMapper.selectWalmartStockPlanByStockId(stockId);

		if (Objects.equals(walmartStockPlan.getStatus(), Constants.DELETED_STATUS)) {
			throw new ServiceException("备货计划已发货，禁止导入备货清单!");
		}

		// 判断库存是否足够能否支持导入

		// 获取导入清单中的所有SKU
		List<String> collect = stockPlanEntities.stream().map(WalmartStockPlanPackingList::getSku).collect(Collectors.toList());
		// 1.根据sku和仓库查询库存
		List<LocalInventoryVO> localInventory = localInventoryMapper.selectSku(collect, walmartStockPlan.getStockWarehouse());

		if (localInventory == null || localInventory.isEmpty()) {
			throw new ServiceException("本次导入的部分sku数据在" + walmartStockPlan.getWarehouseName() + "中不存在！");
		}

		// 2.将导入的清单数据按照sku分组统计数量
		Map<String, Integer> skuQuantityMap = stockPlanEntities.stream()
				.collect(Collectors.groupingBy(
						WalmartStockPlanPackingList::getSku,
						Collectors.summingInt(WalmartStockPlanPackingList::getQuantity)
				));

		// 3.校验是否能够导入清单数据（库存是否足够）
		// 3.1 将库存数据转换为 Map（库存 Map: SKU -> 库存数量）
		Map<String, BigDecimal> inventoryStockMap = localInventory.stream()
				.collect(Collectors.toMap(
						LocalInventoryVO::getSku,
						vo -> BigDecimal.valueOf(vo.getAvailableQuantity()), // 这里使用 lambda 调用方法
						(a, b) -> b
				));


		// 3.2 遍历 skuQuantityMap，检查导入数量是否超出库存
		for (Map.Entry<String, Integer> entry : skuQuantityMap.entrySet()) {
			String sku = entry.getKey();
			BigDecimal importQuantity = BigDecimal.valueOf(entry.getValue()); // 转换为 BigDecimal
			BigDecimal availableStock = inventoryStockMap.getOrDefault(sku, BigDecimal.ZERO); // 获取库存数量，默认 0

			// 3.3 使用 compareTo 进行比较
			if (importQuantity.compareTo(availableStock) > 0) {
				throw new ServiceException("SKU " + sku + " 库存不足，库存: " + availableStock + "，需要: " + importQuantity);
			}
		}

		// 3.4查询到本次导入的sku在其备货计划是否已经导入，如果已经导入了，则判断本次导入的数量和已经在其他备货计划导入的数量加起来是否超过库存
		List<WalmartStockProductDetails> walmartStockProductDetails = walmartStockProductDetailsMapper.selectListNotImport(collect, Constants.DELETED_STATUS, stockId);

		if (!walmartStockProductDetails.isEmpty()) {
			// 3.5得到其他已经导入装箱清单的对应的sku的发货数量
			Map<String, Integer> groupedCountMap = walmartStockProductDetails.stream()
					.collect(Collectors.groupingBy(
							WalmartStockProductDetails::getSku,
							Collectors.summingInt(detail -> detail.getShipmentsProductCount() != null ? detail.getShipmentsProductCount() : 0) // 处理 null
					));

			// 3.6 遍历所有 SKU，检查库存是否足够
			for (String sku : skuQuantityMap.keySet()) {
				int importedQuantity = skuQuantityMap.getOrDefault(sku, 0); // 本次导入数量
				int existingQuantity = groupedCountMap.getOrDefault(sku, 0); // 其他备货计划已导入数量
				BigDecimal availableStock = inventoryStockMap.getOrDefault(sku, BigDecimal.ZERO); // 库存数量

				// 2. 计算总需求量 = 本次导入数量 + 已存在数量
				int totalRequired = importedQuantity + existingQuantity;

				// 3. 判断是否超过库存
				if (BigDecimal.valueOf(totalRequired).compareTo(availableStock) > 0) {
					throw new ServiceException("SKU " + sku + " 在其他备货计划中已经导入导致库存不足，库存: " + availableStock + "，需求: " + totalRequired);
				}
			}
		}


		// 4.1 查询对应的销售计划
		List<Plan> plans = planMapper.selectPlanByIds(shipmentsProducts.stream().map(ShipmentsProduct::getPlanId).collect(Collectors.toList()));

		// 4.2 按照id进行分组
		Map<Long, List<Plan>> groupIdPlan = plans.stream().collect(Collectors.groupingBy(Plan::getId));


		// 2.将导入的清单数据按照sku和GTIN进行分组统计数量
		Map<String, Integer> skuGtinQuantityMap = stockPlanEntities.stream()
				.collect(Collectors.groupingBy(
						item -> item.getSku() + "-" + item.getGtin(),
						Collectors.summingInt(WalmartStockPlanPackingList::getQuantity)
				));

		// 对每个 SKU，按 FIFO 原则分配数量给销售计划明细（shipmentsProduct）
		// shipmentsProducts：所有本次导入的销售计划明细集合
		// groupIdPlan：通过 planId 分组的销售计划，假设每个 planId 只对应一个 Plan 对象，我们用其创建时间排序
		// 用于记录本次分配中实际参与分配的 shipmentsProduct id
		Set<String> allocatedIds = new HashSet<>();
		// 针对每个 SKU 分组
		for (String skuGtin : skuGtinQuantityMap.keySet()) {
			// 获取该 SKU - GTIN 在导入清单中所有的箱序数据
			List<WalmartStockPlanPackingList> packingRows = stockPlanEntities.stream()
					.filter(row -> skuGtin.equals(row.getSku() + "-" + row.getGtin()))
					.sorted(Comparator.comparing(WalmartStockPlanPackingList::getBoxNumber)) // 按箱序排序
					.collect(Collectors.toList());

			// 获取对应的销售计划明细（shipmentsProducts），按 FIFO（例如根据销售计划的创建时间）排序
			List<ShipmentsProduct> productsForSku = shipmentsProducts.stream()
					.filter(sp -> skuGtin.equals(sp.getSku() + "-" + sp.getGtin()))
					.sorted(Comparator.comparing(sp -> {
						Plan plan = groupIdPlan.get(sp.getPlanId()).get(0);
						return plan.getPreparationTime();
					}))
					.collect(Collectors.toList());

			// 为了避免多次分配时重复扣减 shipmentsProduct 的剩余容量，在此使用一个 Map 来记录每个 shipmentsProduct 已经分配的数量
			Map<String, Integer> allocatedMap = new HashMap<>();

			// 针对每个箱序分别处理
			for (WalmartStockPlanPackingList packingRow : packingRows) {
				// 当前箱序要求的数量
				int rowRequired = packingRow.getQuantity();
				// 计算该箱序的每单位运费 = (该箱序总运费 / 该箱序数量)
//				BigDecimal perUnitFreight = packingRow.getFreight()
//						.divide(BigDecimal.valueOf(packingRow.getQuantity()), 6, RoundingMode.UP);

				// 按 FIFO 原则，从所有 shipmentsProducts 中分配数量
				for (ShipmentsProduct sp : productsForSku) {
					// 计算当前明细剩余的可分配数量：计划数量 - 已发数量 - 已分配数量
					int alreadyAllocated = allocatedMap.getOrDefault(sp.getId(), 0);
					int capacity = sp.getPlanSize().intValue() - sp.getSentSize().intValue() - alreadyAllocated;
					if (capacity <= 0) {
						continue;
					}
					// 当前明细能够分配的数量为当前箱序需求与剩余容量的较小值
					int allocate = Math.min(rowRequired, capacity);
					if (allocate > 0) {
						// 更新该 shipmentsProduct 已经分配的数量
						allocatedMap.put(sp.getId(), alreadyAllocated + allocate);
						allocatedIds.add(sp.getId());

						// 根据当前箱序的单价运费计算本次分配的运费：
						// 例如：箱序中总数量为10，总运费100，则单价 = 100/10；若分配 allocate=10，则运费 = 单价*10
//						BigDecimal allocatedFreight = perUnitFreight.multiply(BigDecimal.valueOf(allocate));

						// 更新备货计划明细（更新分配数量和对应的运费）
						walmartStockProductDetailsMapper.update(new WalmartStockProductDetails(),
								new LambdaUpdateWrapper<>(WalmartStockProductDetails.class)
										.setSql("shipments_product_count = shipments_product_count + " + allocate)
//										.setSql("freight = IFNULL(freight, 0) + " + allocatedFreight.toPlainString())
										.set(WalmartStockProductDetails::getFlag, Constants.DELETED_STATUS)
										.eq(WalmartStockProductDetails::getStockId, stockId)
										.eq(WalmartStockProductDetails::getShipmentsProductId, sp.getId())
										.eq(WalmartStockProductDetails::getSku, sp.getSku()));

						// 插入分配记录到新表
						WalmartStockPlanPackingDetail packingDetail = new WalmartStockPlanPackingDetail();
						packingDetail.setStockId(stockId); //备货计划id
						packingDetail.setShipmentsProductId(sp.getId()); // 明细id
						packingDetail.setSku(sp.getSku()); // sku
						packingDetail.setGtin(sp.getGtin()); // GTIN
						packingDetail.setBoxNumber(packingRow.getBoxNumber()); //装箱序号
						packingDetail.setAllocatedQuantity(allocate); // 装箱数量
//						packingDetail.setAllocatedFreight(allocatedFreight); // 本次装箱运费
						packingDetail.setCreatedTime(new Date()); // 创建时间
						packingDetailMapper.insert(packingDetail);
						// 扣减当前箱序还需要分配的数量
						rowRequired -= allocate;
					}
					if (rowRequired == 0) {
						break; // 当前箱序的需求已全部分配完毕
					}
				}
				// 如果该箱序的需求未能全部分配，则说明容量不足，抛出异常
				if (rowRequired > 0) {
					throw new ServiceException(skuGtin + " 在箱序 " + packingRow.getBoxNumber() + " 分配失败：需求 "
							+ packingRow.getQuantity() + " 个，但剩余 " + rowRequired + " 个未能分配到对应的销售计划中");
				}
			}
		}


		// 筛选出在本次分配过程中未曾被选中的 shipmentsProduct id
		List<String> unassignedIds = shipmentsProducts.stream()
				.map(ShipmentsProduct::getId)
				.filter(id -> !allocatedIds.contains(id))
				.collect(Collectors.toList());

		// 删除未导入的明细数据
		if (!unassignedIds.isEmpty()) {
			walmartStockProductDetailsMapper.delete(new LambdaQueryWrapper<>(WalmartStockProductDetails.class)
					.in(WalmartStockProductDetails::getShipmentsProductId, unassignedIds)
					.eq(WalmartStockProductDetails::getStockId, stockId));
		}

//		if(true) throw new ServiceException("");

		// 批量保存导入数据
		return walmartStockPlanPackingListMapper.batchInsert(stockPlanEntities);
//		return 1;
//		Map<String, List<WalmartSkuItemXls>> groupedData = new HashMap<>(); // 存储箱号 -> SKU 列表
//		Map<String, BigDecimal> boxFreightMap = new HashMap<>(); // 存储箱号 -> 运费总和
//		Map<String, String> boxSizeMap = new HashMap<>(); // 存储箱号 -> 尺寸
//		Map<String, String> boxWeightMap = new HashMap<>(); // 存储箱号 -> 重量
//
//		try (InputStream inputStream = file.getInputStream();
//			 Workbook workbook = WorkbookFactory.create(inputStream)) {
//
//			// 获取第一个 sheet
//			Sheet sheet = workbook.getSheetAt(0);
//			int rowCount = sheet.getPhysicalNumberOfRows();
//
//			// 记录当前正在处理的箱号
//			String currentBoxNumber = null;
//
//			// 从第二行开始（跳过表头）
//			for (int i = 1; i < rowCount; i++) {
//				Row row = sheet.getRow(i);
//				if (row == null) {
//					// 如果遇到整行都没数据，就可以选择跳过或直接报错
//					throw new ServiceException("第 " + (i + 1) + " 行不能为空");
//				}
//
//				// 读取单元格
//				Cell boxCell = row.getCell(0);     // 箱序（箱号）
//				Cell skuCell = row.getCell(1);     // SKU
//				Cell quantityCell = row.getCell(2);// 数量
//				Cell sizeCell = row.getCell(3);    // 尺寸
//				Cell weightCell = row.getCell(4);  // 重量
//				Cell freightCell = row.getCell(5); // 运费
//
//				// 1) 判断本行是否有新的箱号
//				boolean hasNewBoxNumber = false;
//				if (boxCell != null && boxCell.getCellType() == CellType.NUMERIC) {
//					int boxNum = (int) boxCell.getNumericCellValue();
//					if (boxNum <= 0) {
//						throw new ServiceException("箱序错误，应为正整数，错误行：" + (i + 1));
//					}
//					currentBoxNumber = String.valueOf(boxNum);
//					hasNewBoxNumber = true;
//				}
//
//				// 如果第一行就没读到任何箱号，那就报错
//				if (currentBoxNumber == null) {
//					throw new ServiceException("首行箱序不能为空，错误行：" + (i + 1));
//				}
//
//				// 2) 读取 SKU（必填）
//				if (skuCell == null
//						|| skuCell.getCellType() != CellType.STRING
//						|| skuCell.getStringCellValue().trim().isEmpty()) {
//					throw new ServiceException("SKU 不能为空，错误行：" + (i + 1));
//				}
//				String sku = skuCell.getStringCellValue().trim();
//
//				// 3) 读取数量（必填）
//				if (quantityCell == null || quantityCell.getCellType() != CellType.NUMERIC) {
//					throw new ServiceException("数量不能为空，且必须为数字，错误行：" + (i + 1));
//				}
//				int quantity = (int) quantityCell.getNumericCellValue();
//				if (quantity <= 0) {
//					throw new ServiceException("数量错误，应为正整数，错误行：" + (i + 1));
//				}
//
//				// 4) 若本行提供了新的箱号，才强制读取尺寸和重量
//				if (hasNewBoxNumber) {
//					// 尺寸
//					if (sizeCell == null
//							|| sizeCell.getCellType() != CellType.STRING
//							|| sizeCell.getStringCellValue().trim().isEmpty()) {
//						throw new ServiceException("尺寸不能为空，错误行：" + (i + 1));
//					}
//					String newBoxSize = sizeCell.getStringCellValue().trim();
//					// 这里可以根据你们的具体格式要求做校验
//					if (!newBoxSize.matches("^\\d+\\*\\d+\\*\\d+$")) {
//						throw new ServiceException("尺寸格式错误，应为 '长*宽*高'，错误行：" + (i + 1));
//					}
//					// 判断是否已存在此箱号的尺寸
//					if (boxSizeMap.containsKey(currentBoxNumber)) {
//						String existingSize = boxSizeMap.get(currentBoxNumber);
//						if (!existingSize.equals(newBoxSize)) {
//							throw new ServiceException("箱序 " + currentBoxNumber
//									+ " 对应的尺寸不一致，错误行：" + (i + 1));
//						}
//					} else {
//						boxSizeMap.put(currentBoxNumber, newBoxSize);
//					}
//
//					// 重量
//					if (weightCell == null
//							|| weightCell.getCellType() != CellType.STRING
//							|| weightCell.getStringCellValue().trim().isEmpty()) {
//						throw new ServiceException("重量不能为空，错误行：" + (i + 1));
//					}
//					String newBoxWeight = weightCell.getStringCellValue().trim();
//					if (!newBoxWeight.matches("^\\d+(\\.\\d{1,2})?KG$")) {
//						throw new ServiceException("重量格式错误，应为 'X.XKG'，错误行：" + (i + 1));
//					}
//					if (boxWeightMap.containsKey(currentBoxNumber)) {
//						String existingWeight = boxWeightMap.get(currentBoxNumber);
//						if (!existingWeight.equals(newBoxWeight)) {
//							throw new ServiceException("箱序 " + currentBoxNumber
//									+ " 对应的重量不一致，错误行：" + (i + 1));
//						}
//					} else {
//						boxWeightMap.put(currentBoxNumber, newBoxWeight);
//					}
//				}
//				// 注意：如果 hasNewBoxNumber == false，就**不强制**读尺寸/重量，
//				// 因为此时表示延续上一行的箱号，尺寸重量也应保持一致。
//
//				// 5) 读取运费（必填）
//				if (freightCell == null) {
//					throw new ServiceException("运费不能为空，错误行：" + (i + 1));
//				}
//				BigDecimal freight;
//				if (freightCell.getCellType() == CellType.NUMERIC) {
//					freight = new BigDecimal(freightCell.getNumericCellValue())
//							.setScale(2, RoundingMode.CEILING);
//				} else if (freightCell.getCellType() == CellType.STRING) {
//					try {
//						freight = new BigDecimal(freightCell.getStringCellValue().trim())
//								.setScale(2, RoundingMode.CEILING);
//					} catch (NumberFormatException e) {
//						throw new ServiceException("运费格式错误，应为数值类型，错误行：" + (i + 1));
//					}
//				} else {
//					throw new ServiceException("运费格式错误，应为数值类型，错误行：" + (i + 1));
//				}
//
//				// 6) 组装 SKU 数据
//				WalmartSkuItemXls skuItem = new WalmartSkuItemXls(sku, quantity, freight);
//				List<WalmartSkuItemXls> skuListForBox =
//						groupedData.computeIfAbsent(currentBoxNumber, k -> new ArrayList<>());
//
//				// 同一箱号不能出现重复SKU
//				if (skuListForBox.stream().anyMatch(item -> item.getSku().equals(sku))) {
//					throw new ServiceException("箱序 " + currentBoxNumber
//							+ " 中不能出现重复的 SKU：" + sku + "，错误行：" + (i + 1));
//				}
//				skuListForBox.add(skuItem);
//
//				// 7) 累加运费
//				boxFreightMap.put(
//						currentBoxNumber,
//						boxFreightMap.getOrDefault(currentBoxNumber, BigDecimal.ZERO).add(freight)
//				);
//			}

		// —— 到这里，整张表就读完了 —
		// groupedData 存着每个箱号对应的所有 SKU 信息
		// boxSizeMap 和 boxWeightMap 分别存尺寸和重量
		// boxFreightMap 存运费总和

//		} catch (IOException e) {
//			// 根据需要处理 IOException 或抛出
//			throw new ServiceException("读取 Excel 文件发生异常：" + e.getMessage());
//		}

//		// **构建并插入数据库**
//		List<WalmartStockPlanPackingList> stockPlanEntities = new ArrayList<>();
//		for (String boxNumber : groupedData.keySet()) {
//			for (WalmartSkuItemXls skuItem : groupedData.get(boxNumber)) {
//				WalmartStockPlanPackingList entity = new WalmartStockPlanPackingList();
//				entity.setBoxNumber(boxNumber);
//				entity.setSku(skuItem.getSku());
//				entity.setQuantity(skuItem.getQuantity());
//				entity.setBoxSize(boxSizeMap.get(boxNumber));
//				entity.setBoxWeight(boxWeightMap.get(boxNumber));
//				entity.setFreight(skuItem.getFreight());
//				entity.setCreatedAt(new Date());
//				entity.setStockId(stockId);
//				stockPlanEntities.add(entity);
//			}
//		}

	}

	/**
	 * 根据 stockId 获取相关的 ShipmentsProduct 列表的辅助方法
	 *
	 * @param stockId 备货计划ID
	 * @return 相关的 ShipmentsProduct 列表，如果找不到则为空列表
	 */
	private List<ShipmentsProduct> getShipmentsProductsByStockId(String stockId) {
		// 1. 查询备货计划明细
		List<WalmartStockProductDetails> detailsList = walmartStockProductDetailsMapper.selectList(
				new LambdaQueryWrapper<>(WalmartStockProductDetails.class)
						.select(WalmartStockProductDetails::getShipmentsProductId) // 优化：只查询需要的字段
						.eq(WalmartStockProductDetails::getStockId, stockId)
		);

		if (detailsList == null || detailsList.isEmpty()) {
			return Collections.emptyList(); // 如果没有明细，直接返回空列表
		}

		// 2. 提取销售计划明细ID
		List<String> shipmentsProductIds = detailsList.stream()
				.map(WalmartStockProductDetails::getShipmentsProductId)
				.distinct() // 去重，可能一个备货计划包含同一个销售计划产品多次？如果不需要去重可以移除
				.collect(Collectors.toList());

		if (shipmentsProductIds.isEmpty()) {
			return Collections.emptyList(); // 以防万一 map 后为空（理论上不会，除非 getShipmentsProductId 返回 null）
		}

		// 3. 查询销售计划明细
		return shipmentsProductMapper.selectListByIds(shipmentsProductIds);
	}

	/**
	 * 根据 stockId 查询相关的 SKU 集合
	 *
	 * @param stockId 备货计划ID
	 * @return SKU 的 Set 集合
	 */
	@Override
	public Set<String> selectSku(String stockId) {
		// 调用辅助方法获取 ShipmentsProduct 列表
		List<ShipmentsProduct> shipmentsProducts = getShipmentsProductsByStockId(stockId);

		if (shipmentsProducts == null || shipmentsProducts.isEmpty()) {
			return Collections.emptySet(); // 返回空集合
		}

		// 提取 SKU 并收集到 Set 中 (直接使用 Collectors.toSet() 更简洁)
		return shipmentsProducts.stream()
				.map(ShipmentsProduct::getSku)
				// .filter(Objects::nonNull) // 如果需要过滤掉 null 的 SKU，取消此行注释
				.collect(Collectors.toSet());
	}

	/**
	 * 根据 stockId 查询相关的 GTIN 集合
	 *
	 * @param stockId 备货计划ID
	 * @return GTIN 的 Set 集合
	 */
	@Override
	public Map<String, ProductVO> selectGTIN(String stockId) {
		// 调用辅助方法获取 ShipmentsProduct 列表
		List<ShipmentsProduct> shipmentsProducts = getShipmentsProductsByStockId(stockId);

		if (shipmentsProducts == null || shipmentsProducts.isEmpty()) {
			return Collections.emptyMap(); // 返回空集合
		}

		List<String> skus = shipmentsProducts.stream().map(ShipmentsProduct::getSku).collect(Collectors.toList());
		// 根据sku查询相关的尺寸和重量
		Map<String, ProductVO> productMap = productMapper.selectSizeAndWeight(skus).stream().collect(Collectors.toMap(ProductVO::getSku, Function.identity(),
				(existing, replacement) -> existing));

		// 分组处理
		// 提取 GTIN 并收集到 Set 中
		Map<String, Set<String>> skuToGtins = shipmentsProducts.stream()
				.filter(p -> p.getSku() != null && !p.getSku().isEmpty()
						&& p.getGtin() != null && !p.getGtin().isEmpty())
				.collect(Collectors.groupingBy(
						ShipmentsProduct::getSku,
						Collectors.mapping(
								ShipmentsProduct::getGtin,
								Collectors.toSet()
						)
				));
		// 将 GTIN 添加到对应的 ProductVO 中
		skuToGtins.forEach((sku, gtins) -> {
			ProductVO vo = productMap.get(sku);
			if (vo != null) {
				vo.setGtins(new ArrayList<>(gtins));
			}
		});

		return productMap;
	}

	@Override
	public int updateStockPlanLogistics(WalmartStockPlan walmartStockPlan) {
		return walmartStockPlanMapper.updateStockPlanLogistics(walmartStockPlan);
	}

	@Override
	public int updateStockPlanVolumeMode(WalmartStockPlan walmartStockPlan) {
		return walmartStockPlanMapper.updateStockPlanVolumeMode(walmartStockPlan);
	}
	/**
	 * 手动绑定备货计划
	 * @param ShipmentStockFilters
	 * @return
	 */
	@Override
	public void bingShipmentStock(ShipmentStockFilters ShipmentStockFilters) {
		String shipmentId = ShipmentStockFilters.getShipmentId();
		String stockId = ShipmentStockFilters.getStockId();
		Long authId = ShipmentStockFilters.getAuthId();


		LocalDateTime deliveryDate = null;
		Shipment s = new Shipment();
		s.setShipmentId(shipmentId);
		Shipment shipment = shipmentMapper.selectShipment(s);
		if(shipment != null){
			deliveryDate = shipment.getActualDeliveryDate();
		}

		// 获取shipment item列表
		ShipmentItemDTO shipmentItem = new ShipmentItemDTO();
		shipmentItem.setShipmentId(shipmentId);
		List<ShipmentItemDTO> shipmentItemList = shipmentItemService.getShipmentItemDTO(shipmentItem);

		// 批量插入的列表
		List<ShipmentStock> batchInsertList = new ArrayList<>();
		for (ShipmentItemDTO item : shipmentItemList) {
			String sku = item.getSku();    // 假设 item 里有 sku 字段
			String gtin = item.getGtin();  // 假设 item 里有 gtin 字段
			Integer qty = Math.toIntExact(item.getItemQty());

			// 使用 MyBatis-Plus 查询 walmart_stock_product_details 获取 freight
			WalmartStockProductDetails stockProductDetails = walmartStockProductDetailsMapper.selectOne(
					new LambdaQueryWrapper<WalmartStockProductDetails>()
							.eq(WalmartStockProductDetails::getStockId, stockId)
							.eq(WalmartStockProductDetails::getSku, sku)
			);
			if (stockProductDetails == null || stockProductDetails.getShipmentsProductCount() == null || stockProductDetails.getShipmentsProductCount() == 0) {
				// 没有详情或分母为 0，则跳过
				continue;
			}

			BigDecimal freight = stockProductDetails.getFreight();
			BigDecimal shipmentsProductCount = BigDecimal.valueOf(stockProductDetails.getShipmentsProductCount());
			BigDecimal firstLeg = freight
					.divide(shipmentsProductCount, 2, RoundingMode.HALF_UP);

			// 使用 MyBatis-Plus 查询 tb_outbound_stock_log 获取 deduction_cost
			List<OutboundStockLog> outboundStockLogs = outboundStockLogMapper.selectList(
					new LambdaQueryWrapper<OutboundStockLog>()
							.eq(OutboundStockLog::getOutboundOrderId, stockId)
							.eq(OutboundStockLog::getSku, sku)
			);

			if (outboundStockLogs.isEmpty()) {
				continue; // 没找到相关出库记录，跳过
			}

			for (OutboundStockLog log : outboundStockLogs) {
				BigDecimal deductionCost = log.getDeductionCost();
				BigDecimal productCost = deductionCost.divide(BigDecimal.valueOf(log.getDeductedQuantity()), 2, RoundingMode.HALF_UP);

				// 构造 WalmartShipmentStock 实体
				ShipmentStock record = new ShipmentStock();
				record.setId(IdWorker.getId());
				record.setProductCost(productCost); // 单个商品的费用
				record.setFirstLeg(firstLeg); // 同一个 freight 对所有记录适用
				record.setSku(sku);
				record.setGtin(gtin);
				record.setStockId(stockId);
				record.setShipmentId(shipmentId);
				record.setQty(qty);
				record.setUnmatchedQty(qty);
				record.setAuthId(authId);
				record.setActualDeliveryDate(deliveryDate);
				batchInsertList.add(record);
			}
		}

		// 批量插入数据库
		if (!batchInsertList.isEmpty()) {
			shipmentStockMapper.batchInsertsShipmentStock(batchInsertList);
		}
	}

	/**
	 * 取消绑定备货计划
	 * @param shipmentId
	 * @return
	 */
	@Override
	@Transactional
	public void cancelShipmentStock(String shipmentId){
		if (shipmentId == null || shipmentId.trim().isEmpty()) {
			// 可以选择抛出一个自定义异常，或者直接 return，避免误删全表
			throw new IllegalArgumentException("shipmentId 不能为空，无法执行删除操作");
		}
		QueryWrapper<ShipmentStock> wrapper = new QueryWrapper<>();
		wrapper.eq("shipment_id", shipmentId);
		int rows = shipmentStockMapper.delete(wrapper);
		System.out.println("删除了 " + rows + " 条 shipment_id=" + shipmentId + " 的记录");
	}

	/**
	 * 修改快递单号
	 * @param walmartStockPlan
	 * @return
	 */
	@Override
	public int updateStockTrackingNumber(WalmartStockPlan walmartStockPlan) {
		return walmartStockPlanMapper.updateStockTrackingNumber(walmartStockPlan);
	}

	/**
	 * 设置walmart备货计划关联ID
	 *
	 * @return
	 */
	@Override
	@Transactional
	public int setWalmartStockPlanShipmentId(String shipmentId, String stockId) {
		// 查询出备货计划
		WalmartStockPlan walmartStockPlan = walmartStockPlanMapper.selectWalmartStockPlanByStockId(stockId);
		if (walmartStockPlan == null) {
			throw new ServiceException("备货计划为空，请联系管理人员核查！");
		}
		walmartStockPlan.setShipmentId(shipmentId);

		return walmartStockPlanMapper.setWalmartStockPlanShipmentId(walmartStockPlan);
	}

	@Override
	public List<WalmartStockPlanPackingList> getImportedPlan(String stockId) {
		// 查询导入的备货清单数据并返回
		return walmartStockPlanPackingListMapper.selectList(new LambdaQueryWrapper<>(WalmartStockPlanPackingList.class)
				.eq(WalmartStockPlanPackingList::getStockId, stockId));
//		List<WalmartStockPlanPackingList> list = new ArrayList<>();
//		for (int i = 0; i < 2; i++) {
//			WalmartStockPlanPackingList walmartStockPlanPackingList = new WalmartStockPlanPackingList();
//			walmartStockPlanPackingList.setStockId("9bb91e11935c431d8d283c8144daac52");
//			walmartStockPlanPackingList.setSku("公模1006");
//			walmartStockPlanPackingList.setBoxNumber("005");
//			walmartStockPlanPackingList.setBoxSize("10*85*46");
//			walmartStockPlanPackingList.setBoxWeight("30KG");
//			walmartStockPlanPackingList.setQuantity(50);
//			walmartStockPlanPackingList.setFreight(BigDecimal.valueOf(100));
//			list.add(walmartStockPlanPackingList);
//		}
//
//		return list;
	}

	@Transactional
	@Override
	public int shippingStockPlan(String[] stockIds) {
		// 1. 校验参数（可选）
		if (stockIds == null || stockIds.length == 0) {
			throw new ServiceException("发货计划ID列表为空，请检查！");
		}

		// 1.2 验证备货计划是否已经选择了计算模式
		List<WalmartStockPlan> walmartStockPlans1 = walmartStockPlanMapper.selectVolumeMode(stockIds);
		walmartStockPlans1.forEach(
				item -> {
					if(item.getVolumeMode() == null){
						throw new ServiceException("备货计划编号：" + item.getStockNumber() + "未选择计算模式，禁止发货!");
					}
				}
		);

		// 2. 验证发货计划是否存在 & 是否已发货
		List<WalmartStockPlan> walmartStockPlans = walmartStockPlanMapper.selectWalmartStockPlanStatus(stockIds);
		if (walmartStockPlans == null || walmartStockPlans.isEmpty()) {
			throw new ServiceException("未查询到对应的备货计划，请检查！");
		}

		Set<String> missingStockIds = new HashSet<>();

		for (WalmartStockPlan plan : walmartStockPlans) {
			if (plan == null) {
				// 这里假设 plan.getStockId() 能返回具体的 ID，否则只能标记“未知”
				missingStockIds.add("未知");
			} else if (Objects.equals(plan.getStatus(), Constants.DELETED_STATUS)) {
				throw new ServiceException("备货计划 " + plan.getStockNumber() + " 已发货！");
			}
		}

		if (!missingStockIds.isEmpty()) {
			throw new ServiceException("数据发生变化，请刷新页面重试!");
		}


		// 查询对应的备货计划明细
		List<WalmartStockProductDetails> details = walmartStockProductDetailsMapper.selectListByStockIds(stockIds);

		// 判断本次发货的备货计划中是否有未导入装箱清单的数据
		boolean hasNull = details.stream()
				.anyMatch(item -> item.getShipmentsProductCount() == null);

		if (hasNull) {
			throw new ServiceException("本次发货的备货计划中包含未导入装箱清单的备货计划！");
		}


		// 按照备货计划id进行分组
		Map<String, WalmartStockPlan> map = walmartStockPlans.stream()
				.collect(Collectors.toMap(WalmartStockPlan::getStockId, Function.identity(), (existing, replacement) -> existing));


		// 根据备货计划进行分组
		Map<String, List<WalmartStockProductDetails>> collect = details.stream().collect(Collectors.groupingBy(WalmartStockProductDetails::getStockId));

		for (Map.Entry<String, List<WalmartStockProductDetails>> stringListEntry : collect.entrySet()) {
			String stockId = stringListEntry.getKey();
			List<WalmartStockProductDetails> walmartStockProductDetails = stringListEntry.getValue();

			// 拿到对应的备货计划信息
			WalmartStockPlan walmartStockPlan = map.get(stockId);
			if (walmartStockPlan == null) {
				throw new ServiceException("备货计划不存在，StockID = " + stockId);
			}

			// 创建插入出库单的对应集合
			List<OutboundOrderDetail> detailList = new ArrayList<>();
			// 总出库量
			AtomicInteger totalQuantity = new AtomicInteger(0);
			// 总出库成本
			AtomicReference<BigDecimal> totalAmount = new AtomicReference<>(BigDecimal.ZERO);
			// 修改销售计划明细的发货数量
			walmartStockProductDetails.forEach(
					item -> {
						Integer shipmentsProductCount = item.getShipmentsProductCount(); // 当前sku的发货数量
						shipmentsProductMapper.update(new ShipmentsProduct(), new LambdaUpdateWrapper<ShipmentsProduct>()
								.setSql("sent_size = sent_size + " + shipmentsProductCount)
								.setSql("oper_size = oper_size - " + shipmentsProductCount)
								.eq(ShipmentsProduct::getProductId, item.getProductId())
								.eq(ShipmentsProduct::getId, item.getShipmentsProductId()));
						// 插入出库日志表相关数据
						// 1. 先查询该sku在入库数据表中是否有剩余库存大于0的记录
						List<InboundStock> inboundStocks = inboundStockMapper.selectList(new LambdaQueryWrapper<InboundStock>()
								.eq(InboundStock::getWarehouseId, walmartStockPlan.getStockWarehouse())
								.eq(InboundStock::getSku, item.getSku())
								.gt(InboundStock::getRemainingQuantity, 0)
								.orderByAsc(InboundStock::getReceivedDate)
						);


						int currentShipmentsProductCount = item.getShipmentsProductCount(); // 需要扣减的数量

						if (inboundStocks.isEmpty()) {
							throw new ServiceException("入库日志表中无可用库存记录 (SKU: " + item.getSku() + ")，发货失败");
						}

						// 2. 如果说有记录，则需要判断相关记录是否足够扣减
						for (InboundStock inboundStock : inboundStocks) {
							if (currentShipmentsProductCount <= 0) { // 已满足此商品的数量
								break;
							}
							int quantityToDeductFromThisBatch = Math.min(currentShipmentsProductCount, inboundStock.getRemainingQuantity());

							// 计算本次参与扣减的入库记录的扣减成本
							BigDecimal costFromThisBatch = BigDecimal.valueOf(quantityToDeductFromThisBatch).multiply(inboundStock.getUnitCost());

							// 更新数据库中当前入库库存的剩余数量
							int newRemainingQuantityForInbound = inboundStock.getRemainingQuantity() - quantityToDeductFromThisBatch;
							inboundStockMapper.update(null, new LambdaUpdateWrapper<InboundStock>()
									.set(InboundStock::getRemainingQuantity, newRemainingQuantityForInbound)
									.eq(InboundStock::getId, inboundStock.getId()));

							currentShipmentsProductCount -= quantityToDeductFromThisBatch;

							// 插入出库日志记录
							OutboundStockLog outboundStockLog = new OutboundStockLog();
							outboundStockLog.setOutboundOrderId(walmartStockPlan.getStockId()); // 备货计划id
							outboundStockLog.setBatchId(inboundStock.getId()); // 入库日志id
							outboundStockLog.setDeductedQuantity(quantityToDeductFromThisBatch);  // 扣减数量
							outboundStockLog.setDeductedDate(new Date()); // 扣减时间
							outboundStockLog.setDeductionCost(costFromThisBatch); // 本次扣减成本
							outboundStockLog.setRemark("备货计划id：" + walmartStockPlan.getStockId() + "出库SKU:" + item.getSku() + ":" + item.getShipmentsProductCount() + "个");
							outboundStockLog.setSku(item.getSku()); // 关联的SKU
							outboundStockLogMapper.insert(outboundStockLog);
						}

						// 检查此sku的全部数量是否已满足。
						if (currentShipmentsProductCount > 0) {
							throw new ServiceException("入库日志表中总库存不足 (SKU: " + item.getSku() +
									", 需求: " + item.getShipmentsProductCount() +
									", 尚缺: " + currentShipmentsProductCount + ")，发货失败");
						}
						// 插入出库单明细
						// 1.1 查询当前sku在出库日志中的所有出库明细
						List<OutboundStockLog> outboundStockLogs = outboundStockLogMapper.selectList(
								new LambdaQueryWrapper<OutboundStockLog>()
										.eq(OutboundStockLog::getOutboundOrderId, walmartStockPlan.getStockId())
										.eq(OutboundStockLog::getSku, item.getSku())
						);
						int sum = outboundStockLogs.stream().filter(a -> a.getDeductedQuantity() != null)
								.mapToInt(OutboundStockLog::getDeductedQuantity).sum();

						BigDecimal reduce = outboundStockLogs.stream().map(OutboundStockLog::getDeductionCost)
								.filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);

						totalQuantity.addAndGet(sum); // 累加总数量
						totalAmount.updateAndGet(current -> current.add(reduce)); // 累加总成本

						OutboundOrderDetail detail = new OutboundOrderDetail();
						detail.setProductId(Long.valueOf(item.getProductId())); // 产品id
						detail.setSku(item.getSku()); // sku
						detail.setQuantity(sum); // 当前sku的扣减数量
						detail.setAmount(reduce); // 扣减总成本
						detailList.add(detail);
					}
			);

			// 创建对应的出库单
			OutboundOrder outboundOrder = new OutboundOrder();
			outboundOrder.setOutboundNo(generateCodeInbound(1).get(0)); // 出库单号
			outboundOrder.setWarehouseId(walmartStockPlan.getStockWarehouse()); // 仓库id
			outboundOrder.setOutboundType(Constants.OUTBOUND_TYPE_DELIVERY); // 出库类型：发货出库
			outboundOrder.setStatus(1); // 出库状态为已出库
			outboundOrder.setRelatedOrderNo(walmartStockPlan.getStockNumber()); // 关联备货单据号
			outboundOrder.setTotalQuantity(totalQuantity.get()); // 出库总数量
			outboundOrder.setTotalAmount(totalAmount.get()); // 出库总成本
			outboundOrder.setCreator(SecurityUtils.getNickname()); // 创建人
			outboundOrder.setCreateTime(new Date()); // 创建时间
			outboundOrderMapper.insertOutboundOrder(outboundOrder);
			Long outboundId = outboundOrder.getOutboundId(); // 出库单id

			// 设置出库单id到详细中
			detailList.forEach(
					b -> b.setOutboundId(outboundId)
			);

			// 批量插入出库单详细
			outboundOrderDetailMapper.insertBatch(detailList);

			// 汇总该对应备货计划下的sku对应的发货数量
			Map<String, Integer> skuCountMap = walmartStockProductDetails.stream()
					.collect(Collectors.groupingBy(
							WalmartStockProductDetails::getSku,
							Collectors.summingInt(WalmartStockProductDetails::getShipmentsProductCount)
					));

			for (Map.Entry<String, Integer> stringIntegerEntry : skuCountMap.entrySet()) {
				String sku = stringIntegerEntry.getKey(); // sku
				Integer count = stringIntegerEntry.getValue(); // 发货数量
				// 扣减对应的库存
				// 1. 查询并加锁，获取库存信息
				LocalInventory inventory = localInventoryMapper.selectBySkuAndWarehouse(sku, walmartStockPlan.getStockWarehouse());

				if (inventory != null) {
					// 2. 使用乐观锁更新库存
					BigDecimal inspectedQty = BigDecimal.valueOf(count);
					BigDecimal previousQty = inventory.getActualQuantity();
					BigDecimal newQty = previousQty.subtract(inspectedQty);

					int rows = localInventoryMapper.updateLocalInventoryBySku(
							sku,
							walmartStockPlan.getStockWarehouse(),
							inventory.getVersion(),
							inspectedQty.negate()
					);
					if (rows == 0) {
						// 乐观锁失败，版本不匹配
						throw new ServiceException("库存更新失败，请重试");
					}

					// 3. 插入库存变动明细
					insertInventoryDetails(
							inventory.getInventoryId(),
							sku,
							walmartStockPlan.getStockWarehouse().longValue(),
							walmartStockPlan.getRemark(),
							InventoryChangeType.SHIPMENT_OUTBOUND.getCode(),
							inspectedQty,
							previousQty,
							newQty,
							walmartStockPlan.getStockNumber()
					);

				} else {
					throw new ServiceException("更新的库存信息不存在!");
				}
			}
		}


		// 更新备货计划状态
		int result = walmartStockPlanMapper.updateStatus(stockIds);
		/*
		 * 销售计划明细与销售计划发货状态更新逻辑：
		 */
		List<ShipmentsProduct> salesPlanDetails = shipmentsProductMapper.selectListByIds(details.stream().map(WalmartStockProductDetails::getShipmentsProductId).collect(Collectors.toList()));
		if (salesPlanDetails != null && !salesPlanDetails.isEmpty()) {
			// 更新每个销售计划明细的发货状态
			for (ShipmentsProduct detail : salesPlanDetails) {
				// 为防止 null 值的问题，需确保 shippedQuantity 有初始值
				int shipped = detail.getSentSize() == null ? 0 : detail.getSentSize().intValue();
				int planned = detail.getPlanSize().intValue();
				if (shipped == planned) {
					detail.setShippingStatus(Constants.SHIPPED_STATUS); // 已发货
				} else if (shipped > 0) {
					detail.setShippingStatus(Constants.PARTIAL_SHIP_STATUS); // 部分发货
				} else {
					detail.setShippingStatus(Constants.PREPARING_STATUS); // 备货中
				}
				shipmentsProductMapper.updateById(detail);
			}

			/*
			 * 	按所属销售计划分组，判断每个销售计划下所有明细的状态，从而更新销售计划状态：
			 *    - 如果所有明细状态都是“已发货”，则销售计划状态更新为“已发货”。
			 *    - 如果所有明细状态都是“备货中”，则销售计划状态更新为“备货中”。
			 *    - 如果明细中存在部分发货，或者既有发货也有待发货的情况，则销售计划状态更新为“部分发货”。
			 */
			// 收集所有涉及的销售计划ID，假设每个 SalesPlanDetail 中有 salesPlanId 字段
			Set<Long> salesPlanIds = salesPlanDetails.stream()
					.map(ShipmentsProduct::getPlanId)
					.collect(Collectors.toSet());
			for (Long salesPlanId : salesPlanIds) {
				// 根据销售计划id查询对应的销售计划明细
				List<ShipmentsProduct> detailList = shipmentsProductMapper.selectList(new LambdaQueryWrapper<ShipmentsProduct>()
						.eq(ShipmentsProduct::getPlanId, salesPlanId)
						.eq(ShipmentsProduct::getDeleteStatus, Constants.NOT_DELETED_STATUS));

				// 判断所有明细中的状态是否都为已发货
				boolean allShipped = detailList.stream()
						.allMatch(d -> Constants.SHIPPED_STATUS.equals(d.getShippingStatus()));
				// 判断所有明细中的状态是否都为备货中
				boolean allPreparing = detailList.stream()
						.allMatch(d -> Constants.PREPARING_STATUS.equals(d.getShippingStatus()));
				// 如果不是全“已发货”或全“备货中”，则认为存在部分发货情况
//				boolean partial = !allShipped && !allPreparing;
				Plan salesPlan = planMapper.selectById(salesPlanId);
				if (allShipped) {
					salesPlan.setShippingStatus(Constants.SHIPPED_STATUS);
				} else if (allPreparing) {
					salesPlan.setShippingStatus(Constants.PREPARING_STATUS);
				} else {
					// 如果既不是已发货也不是备货中那么就是部分发货的状态
					salesPlan.setShippingStatus(Constants.PARTIAL_SHIP_STATUS);
				}
				planMapper.updateById(salesPlan);
			}
		}
//		if(true){
//			throw new ServiceException("");
//		}
		return result;
	}

	private List<String> generateCodeInbound(Integer count) {
		synchronized (this) {  // 使用同步块，防止多线程并发访问
			// 获取当前日期
			SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
			String currentDate = dateFormat.format(new Date());

			// 获取最后一个收货单编号，在事务中加锁（使用 FOR UPDATE）
			String lastStockNumber = outboundOrderMapper.selectLastOutboundNumberForUpdate(currentDate);

			// 生成的编号列表
			List<String> generatedCodes = new ArrayList<>();

			// 如果没有找到最后一个编号，或者日期不匹配，说明是当天的第一个收货单
			String number;
			if (StringUtils.isEmpty(lastStockNumber) || !lastStockNumber.substring(2, 10).equals(currentDate)) {
				// 如果没有找到或者日期不匹配，生成从BS202411290001开始
				number = Constants.INBOUND_NUMBER;
			} else {
				// 提取编号中的序列部分，并加一
				String lastNumberStr = lastStockNumber.substring(10); // 编号格式：CR + 日期（8位） + 序号（4位）
				long lastNumber = Long.parseLong(lastNumberStr);
				number = String.format("%04d", lastNumber + 1);
			}

			// 生成指定数量的编号
			for (int i = 0; i < count; i++) {
				String code = Constants.OUTBOUND_ORDER_NUMBER + currentDate + number;
				// 检查生成的编号是否已存在，避免并发冲突
				int retryCount = 0;
				while (outboundOrderMapper.checkOutboundNumberExists(code) > 0 && retryCount < 5) {
					// 如果编号已经存在，重新生成
					retryCount++;
					String lastReceiptNumberRetry = outboundOrderMapper.selectLastOutboundNumberForUpdate(currentDate);
					if (StringUtils.isEmpty(lastReceiptNumberRetry) || !lastReceiptNumberRetry.substring(2, 10).equals(currentDate)) {
						number = Constants.INBOUND_NUMBER;
					} else {
						String lastNumberStrRetry = lastReceiptNumberRetry.substring(10);
						long lastNumberRetry = Long.parseLong(lastNumberStrRetry);
						number = String.format("%04d", lastNumberRetry + 1);
					}
					code = Constants.OUTBOUND_ORDER_NUMBER + currentDate + number;
				}

				if (retryCount >= 5) {
					// 达到重试次数限制，可以抛出异常或返回失败标志
					throw new RuntimeException("Failed to generate unique receipt number after 5 retries.");
				}

				// 将生成的编号添加到列表
				generatedCodes.add(code);

				// 更新编号的序号
				long currentNumber = Long.parseLong(number);
				number = String.format("%04d", currentNumber + 1);
			}

			return generatedCodes;
		}
	}


	private void insertInventoryDetails(
			String inventoryId,
			String sku,
			Long warehouseId,
			String remark,
			int changeType,
			BigDecimal quantity,
			BigDecimal previousQuantity,
			BigDecimal postChangeQuantity,
			String storageNumber
	) {
		// 这里插入库存变动明细
		LocalInventoryDetails details = new LocalInventoryDetails();
		details.setInventoryDetailId(IdUtils.fastSimpleUUID());
		details.setInventoryId(inventoryId);
		details.setSku(sku);
		details.setWarehouseId(warehouseId);
		details.setOperatorName(SecurityUtils.getNickname()); // 设置操作人为当前操作人
		details.setOperatingTime(new Date()); // 操作时间
		details.setChangeType(changeType); // 变动类型为入库
		details.setQuantity(quantity.negate()); // 变动数量
		details.setNotes(remark); // 备注
		details.setPreviousQuantity(previousQuantity); // 变动前的数量
		details.setPostChangeQuantity(postChangeQuantity); // 变动后的数量
		details.setReferenceId(storageNumber); // 关联的入库单
		localInventoryDetailsMapper.insertLocalInventoryDetails(details); // 插入变动记录
	}
}